React基础笔记(三)

这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战vue

前言

本文做为本人学习总结之用,同时分享给你们,适合入门的react小白
由于我的技术有限,若是有发现错误或存在疑问之处,欢迎指出或指点!不胜感谢!react

redux

基本理解

学习文档

redux

  1. redux是一个专门用于作管理状态的JS库 (不是react插件库)
  2. 它能够用在react, angular, vue等项目中, 但基本与react配合使用。
  3. 做用: 集中式管理react应用中多个组件的状态。

什么状况下须要使用redux

  1. 某个组件的状态,须要让其余组件能够随时拿到(共享)。
  2. 一个组件须要改变另外一个组件的状态(通讯)。
  3. 整体原则:能不用就不用, 若是不用比较吃力才考虑使用。

工做流程

redux的三个核心概念

action

  1. 动做的对象git

  2. 包含两个属性github

    • type:标识属性, 值为字符串, 惟一, 必要属性
    • data:数据属性, 值类型任意, 可选属性
  3. 编码:ajax

//该模块是用于定义action对象中type类型的常量值,目的只有一个:便于管理的同时防止单词写错
import {ADD_PERSON} from '../constant'
//专门为组件生成action对象
export const addPerson = data => ({type:ADD_PERSON,data})
复制代码

reducer

  1. 用于初始化状态、加工状态。
  2. 加工时,根据旧的state和action, 产生新的state的纯函数
const initState = xx
export default function xxxReducer(preState =initState, action) {
  const {type ,data} = action
  switch (type) {
    case JIA:
      return preState+1
    default :
      return preState
  }
}
复制代码

store

  1. 将state、action、reducer联系在一块儿的对象
  2. 如何获得此对象?
//引入createStore,专门用于建立redux中最为核心的store对象
import {createStore} from 'redux'
//引入为Count组件服务的reducer
import countReducer from './count_reducer'
//暴露store
export default createStore(countReducer)
复制代码
  1. 此对象的功能?npm

    • getState(): 获得state
    • dispatch(action): 分发action, 触发reducer调用, 产生新的state
    • subscribe(listener): 注册监听, 当产生了新的state时, 自动调用
componentDidMount(){
    //检测redux中状态的变化,只要变化,就调用render
    store.subscribe(()=>{
            this.setState({})
    })
}
store.dispatch(createIncrementAction(value*1))
store.getState()
复制代码

redux核心api

createStore

做用:建立包含指定reducer的store对象redux

6.3.2 store对象

  1. 做用: redux库最核心的管理对象
  2. 它内部维护着:
    1. state
    2. reducer
  3. 核心方法:
    1. getState()
    2. dispatch(action)
    3. subscribe(listener)
  4. 具体编码:
    1. store.getState()
    2. store.dispatch({type:'INCREMENT', number})
    3. store.subscribe(render)

applyMiddleware()

做用:应用上基于redux的中间件(插件库)api

combineReducers()

做用:合并多个reducer函数数组

redux异步

1. redux默认是不能进行异步处理的,缓存

2. 某些时候应用中须要在redux 中执行异步任务(ajax, 定时器)

必须下载插件:

npm install --save redux-thunk

在store.js文件中引入redux-thunk,用于支持异步action
import thunk from 'redux-thunk'

react-redux (关键)

理解

  1. 一个专门的react插件
  2. 专门简化react应用中使用redux

react-redux组件拆分两大类

UI组件

  • 只负责 UI 的呈现,不带有任何业务逻辑    
  • 经过props接收数据(通常数据和函数)
  • 不使用任何 Redux 的 API
  • 通常保存在components文件夹下

容器组件

  • 负责管理数据和业务逻辑,不负责UI的呈现
  • 使用 Redux 的 API
  • 通常保存在containers文件夹下

相关API

  1. Provider:让全部组件均可以获得state数据
import {Provider} from 'react-redux'
<Provider store={store}>
  <App/>
</Provider>
复制代码
  1. connect:用于包装 UI 组件生成容器组件
  2. mapStateToprops:将外部的数据(即state对象)转换为UI组件的标签属性
  3. mapDispatchToProps:将分发action的函数转换为UI组件的标签属性
//引入Count的UI组件
import CountUI from '../../components/Count'
//引入action
import {
	createIncrementAction,
} from '../../redux/count_action'

//引入connect用于链接UI组件与redux
import {connect} from 'react-redux'

/* 1.mapStateToProps函数返回的是一个对象; 2.返回的对象中的key就做为传递给UI组件props的key,value就做为传递给UI组件props的value 3.mapStateToProps用于传递状态 */
function mapStateToProps(state){
	return {count:state}
}

/* 1.mapDispatchToProps函数返回的是一个对象; 2.返回的对象中的key就做为传递给UI组件props的key,value就做为传递给UI组件props的value 3.mapDispatchToProps用于传递操做状态的方法 */
function mapDispatchToProps(dispatch){
	return {
		jia:number => dispatch(createIncrementAction(number))
	}
}

//使用connect()()建立并暴露一个Count的容器组件
export default connect(mapStateToProps,mapDispatchToProps)(CountUI)

//简写
export default connect(
	state => ({count:state}),
  {jia:createIncrementAction}
)(Count)
复制代码

setState

  1. setState(stateChange, [callback])------对象式的setState

    1.stateChange为状态改变对象(该对象能够体现出状态的更改)
    2.callback是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用
    复制代码
  2. setState(updater, [callback])------函数式的setState

    1.updater为返回stateChange对象的函数。
    2.updater能够接收到state和props。
    3.callback是可选的回调函数, 它在状态更新、界面也更新后(render调用后)才被调用。
    复制代码

总结:

  1. 对象式的setState是函数式的setState的简写方式(语法糖)
  2. 使用原则:
    • 若是新状态不依赖于原状态 ===> 使用对象方式
    • 若是新状态依赖于原状态 ===> 使用函数方式
    • 若是须要在setState()执行后获取最新的状态数据,
      要在第二个callback函数中读取
//对象式的setState
//1.获取原来的count值
const {count} = this.state
//2.更新状态
this.setState({count:count+1},()=>{
  console.log(this.state.count);
})
//console.log('12行的输出',this.state.count); //0 

//函数式的setState
this.setState( (state,props) => ({count:state.count+1}),() =>{
  console.log(state.count)
})
复制代码

lazyLoad 路由懒加载

//1.经过React的lazy函数配合import()函数动态加载路由组件 ===> 路由组件代码会被分开打包
   const Login = lazy(()=>import('@/pages/Login'))
   
   //2.经过<Suspense>指定在加载获得路由打包文件前显示一个自定义loading界面
   fallback包裹一个通常组件也能够
   <Suspense fallback={<h1>loading.....</h1>}>
        <Switch> <Route path="/xxx" component={Xxxx}/> <Redirect to="/login"/> </Switch>
    </Suspense>
复制代码

Hooks

  1. React Hook/Hooks是什么?

    • Hook是React 16.8.0版本增长的新特性/新语法
    • 可让你在函数组件中使用 state 以及其余的 React 特性
  2. 三个经常使用的Hook

    • State Hook: React.useState()
    • Effect Hook: React.useEffect()
    • Ref Hook: React.useRef()
  3. State Hook

    • State Hook让函数组件也能够有state状态, 并进行状态数据的读写操做
    • 语法: const [xxx, setXxx] = React.useState(initValue)
    • useState()说明:
      参数: 第一次初始化指定的值在内部做缓存
      返回值: 包含2个元素的数组, 第1个为内部当前状态值, 第2个为更新状态值的函数\
  4. setXxx()2种写法:

    • setXxx(newValue): 参数为非函数值, 直接指定新的状态值, 内部用其覆盖原来的状态值
    • setXxx(value => newValue): 参数为函数, 接收本来的状态值, 返回新的状态值, 内部用其覆盖原来的状态值
  5. Effect Hook

    • Effect Hook 可让你在函数组件中执行反作用操做(用于模拟类组件中的生命周期钩子)
    • React中的反作用操做:
      • 发ajax请求数据获取
      • 设置订阅 / 启动定时器
      • 手动更改真实DOM
    • 语法和说明:
    useEffect(() => { 
      // 在此能够执行任何带反作用操做
      return () => { // 在组件卸载前执行
        // 在此作一些收尾工做, 好比清除定时器/取消订阅等
      }
    }, [stateValue]) // 若是指定的是[], 回调函数只会在第一次render()后执行
    复制代码
    • 能够把 useEffect Hook 看作以下三个函数的组合

      componentDidMount()
      componentDidUpdate()
      componentWillUnmount()

  6. Ref Hook

    • Ref Hook能够在函数组件中存储/查找组件内的标签或任意其它数据
    • 语法: const refContainer = useRef()
    • 做用:保存标签对象,功能与React.createRef()同样
function Demo(){
	const [count,setCount] = React.useState(0)
	const myRef = React.useRef()

	React.useEffect(()=>{
		let timer = setInterval(()=>{
			setCount(count => count+1 )
		},1000)
		return ()=>{
			clearInterval(timer)
		}
	},[])

	//加的回调
	function add(){
		//setCount(count+1) //第一种写法
		setCount(count => count+1 )
	}

	//提示输入的回调
	function show(){
		alert(myRef.current.value)
	}

	//卸载组件的回调
	function unmount(){
		ReactDOM.unmountComponentAtNode(document.getElementById('root'))
	}

	return (
		<div> <input type="text" ref={myRef}/> <h2>当前求和为:{count}</h2> <button onClick={add}>点我+1</button> <button onClick={unmount}>卸载组件</button> <button onClick={show}>点我提示数据</button> </div>
	)
}
复制代码

Fragment

做用:能够不用必须有一个真实的DOM根标签了

<Fragment key={1}>  //能参与遍历
  <input type="text"/>
  <input type="text"/>
</Fragment>
或者
<>
  <input type="text"/>
  <input type="text"/>
</>
复制代码

Context

一种组件间通讯方式, 经常使用于【祖组件】与【后代组件】间通讯

  1. 建立Context容器对象:

  const XxxContext = React.createContext()

  1. 渲染子组时,外面包裹xxxContext.Provider, 经过value属性给后代组件传递数据:

  <xxxContext.Provider value={数据}>子组件</xxxContext.Provider>

  1. 后代组件读取数据:

    //第一种方式:仅适用于类组件
     static contextType = xxxContext  // 声明接收context
     this.context // 读取context中的value数据
    
    
    //第二种方式: 函数组件与类组件均可以
    <xxxContext.Consumer>
    {
      value => ( // value就是context中的value数据
          要显示的内容
      )
    }
    </xxxContext.Consumer>
    复制代码

注意:在应用开发中通常不用context, 通常都它的封装react插件

//建立Context对象
const MyContext = React.createContext()
const {Provider,Consumer} = MyContext
export default class A extends Component {

	state = {username:'tom',age:18}
	render() {
		const {username,age} = this.state
		return (
			<div className="parent"> <h3>我是A组件</h3> <h4>个人用户名是:{username}</h4> <Provider value={{username,age}}> <B/> </Provider> </div>
		)
	}
}

class B extends Component {
	render() {
		return (
			<div className="child"> <h3>我是B组件</h3> <C/> </div>
		)
	}
}

 /* class C extends Component { //声明接收context static contextType = MyContext render() { const {username,age} = this.context return ( <div className="grand"> <h3>我是C组件</h3> <h4>我从A组件接收到的用户名:{username},年龄是{age}</h4> </div> ) } } */

function C(){
	return (
		<div className="grand"> <h3>我是C组件</h3> <h4>我从A组件接收到的用户名: <Consumer> {value => `${value.username},年龄是${value.age}`} </Consumer> </h4> </div>
	)
}
复制代码

组件优化 Component

项目的2个问题

  1. 只要执行setState(),即便不改变状态数据, 组件也会从新render()

  2. 只当前组件从新render(), 就会自动从新render子组件 ==> 效率低

效率高的作法:

只有当组件的state或props数据发生改变时才从新render()

缘由:

Component中的shouldComponentUpdate()老是返回true

解决:

  办法1:

重写shouldComponentUpdate()方法
 比较新旧state或props数据, 若是有变化才返回true, 若是没有返回false
复制代码

  办法2:  

使用PureComponent
 PureComponent重写了shouldComponentUpdate(), 只有state或props数据有变化才返回true
 注意:
    只是进行state和props数据的浅比较, 若是只是数据对象内部数据变了, 返回false  
    不要直接修改state数据, 而是要产生新数据
    
复制代码

项目中通常使用PureComponent来优

import React, { PureComponent } from 'react'
export default class Parent extends PureComponent {
	state = {carName:"奔驰c36",stus:['小张','小李','小王']}
	addStu = ()=>{
		const {stus} = this.state
		this.setState({stus:['小刘',...stus]})
	}

	changeCar = ()=>{
		this.setState({carName:'迈巴赫'})
	}
	/* shouldComponentUpdate(nextProps,nextState){ // console.log(this.props,this.state); //目前的props和state // console.log(nextProps,nextState); //接下要变化的目标props,目标state return !this.state.carName === nextState.carName } */
	render() {
		console.log('Parent---render');
		const {carName} = this.state
		return (
			<div className="parent"> <h3>我是Parent组件</h3> {this.state.stus}&nbsp; <span>个人车名字是:{carName}</span><br/> <button onClick={this.changeCar}>点我换车</button> <button onClick={this.addStu}>添加一个小刘</button> <Child carName="奥拓"/> </div>
		)
	}
}

class Child extends PureComponent {
	/* shouldComponentUpdate(nextProps,nextState){ console.log(this.props,this.state); //目前的props和state console.log(nextProps,nextState); //接下要变化的目标props,目标state return !this.props.carName === nextProps.carName } */
	render() {
		console.log('Child---render');
		return (
			<div className="child"> <h3>我是Child组件</h3> <span>我接到的车是:{this.props.carName}</span> </div>
		)
	}
}
复制代码

\

render props

如何向组件内部动态传入带内容的结构(标签)?

  Vue中:

     使用slot技术, 也就是经过组件标签体传入结构  <AA><BB/></AA>

  React中:

- 使用children props: 经过组件标签体传入结构
- 使用render props: 经过组件标签属性传入结构, 通常用render函数属性
复制代码

children props

  <A>
    <B>xxxx</B>
  </A>
  {this.props.children}
复制代码

  问题: 若是B组件须要A组件内的数据, ==> 作不到

render props

  <A render={(data) => <C data={data}></C>}></A>

  A组件: {this.props.render(内部state数据)}

  C组件: 读取A组件传入的数据显示 {this.props.data}

错误边界

理解:

错误边界:用来捕获后代组件错误,渲染出备用页面

特色:

只能捕获后代组件生命周期产生的错误,不能捕获本身组件产生的错误和其余组件在合成事件、定时器中产生的错误

使用方式:

getDerivedStateFromError配合componentDidCatch
// 生命周期函数,一旦后台组件报错,就会触发
//当Parent的子组件出现报错时候,会触发getDerivedStateFromError调用,并携带错误信息
static getDerivedStateFromError(error){
  console.log('@@@',error);
  return {hasError:error}
}

componentDidCatch(){
  console.log('此处统计错误,反馈给服务器,用于通知编码人员进行bug的解决');
}
复制代码
相关文章
相关标签/搜索