这是我参与8月更文挑战的第10天,活动详情查看:8月更文挑战
React扩展
setState、lazyLoad、Context、组件优化、PureComponent、错误边界的使用
一、setState
对象式的setState是函数式的setState的语法糖,如果新状态依赖于原状态就使用对象方式,如果依赖就使用函数方式(比如下面这个例子,把count改成count+1);
-
对象式的
setState:const {count} = this.state; //count=0 setState(count:count+1, [callback]); console.log(this.state.count); // 0这个我们平时是经常用的,
stateChange就是需要改变的状态,只是我们平时是很少在后面加一个callback,这是个可选的回调函数,它在状态更新完毕、界面也更新后(render调用后)才被调用。由于我们
setState里面的东西是异步被执行的,所以在setState的下一个语句的那个this.state.count还是原来没有被修改的那个值,如果想要通过this.state.count来获取到修改后的值并且进行处理,就可以调用这个回调函数callback。 -
函数式的
setState:this.setState(state => ({ count: state.count + 1 }));函数式的
setState的第一个参数就是一个updater函数,可以接收到state和props,因此不需要提前从this.state中拿值,第二个参数和上面说的回调函数一样。
二、lazyLoad
有时候在我们路由很多的时候,我们不想要在第一次进入页面的时候就将所有路由加载完成,而是等到我们要用到它的时候再进行加载,这个时候就要用到懒加载。
引用react中的一个lazy函数,并且用lazy来引入组件
import React, { Component, lazy, Suspense } from 'react'
const Home = lazy(() => import('./Home')) //懒加载的组件
除此之外,还要用Suspense组件把所有注册路由的地方包起来,然后要给Suspense里面指定一个fallback(可以是一个组件);
<Suspense fallback={<h1>loading...</h1>}>
<Route path="/home" component={Home}>
</Suspense>
三、Fragment
在render的时候我们经常要用的一个div作为最外层包裹的标签,在页面中渲染之后这个div可能没有什么用,所以如果我们不想要有这些多余的div,就可以用<Fragment>标签来代替,或者直接用<>空标签来代替。
<Fragment key={111}></Fragment>
<></>
但是空标签是没办法放值的,比如Fragment可以放key值而空标签是不可以的。
四、Context
context是一种组件间通信方式,常用于【祖孙组件】或【祖祖孙组件】之间进行通信。
可能会存在一种情况:A组件中有B,B组件中有C组件;A要将值传给C组件,但是B组件不需要这个值;在这种情况下就可以用到Context,使用步骤如下:
-
创建Context对象
const MyContext = React.createContext(); const {Provider, Consumer} = MyContext; //在A中: <Provider value={{username, age}}> <B/> </Provider>这样子设置之后,不用再在B组件和对B组件中的C组件进行处理,B组件及其子组件可以接收到这个value值。一个
Context也是可以传多个值的;-
第一种(仅适用于类组件):通过
static contextType = myContext来声明接收context(一定要声明接收才能接收到),在组件的this.context里面就可以找到这个username; -
第二种:函数组件与类组件都可以使用。比第一种方法需要多一个
Consumer组件<Consumer> { value=> { return `${value.username}, ${value.age}`; } } </Consumer>
在应用开发中一般不用Context,一般用它的封装react插件,比如
react-redux。 -
五、组件优化
Component存在的问题
- 只要执行
setState,即使不改变状态数据,组件也会重新render() - 只要当前组件重新
render(),即使子组件没有用到父组件的任何数据,还是会自动重新render子组件
这两种情况都会导致效率低,都是由于Component中的shouldComponentUpdate()总是返回true。如果我们想要提高效率,就要让它只要当组件的state或者props数据发生改变的时候再重新render(),要解决这个问题,有两个方法:
重写shouldComponentUpdate()方法(PureComponent)
这个方法可以拿到即将要变化的props和state,在这个时候,我们可以将即将变化的props和state的和当前的props和state进行比较,如果是一样就返回false,进而阻止render()的进行。
shouldComponentUpdate(nextProps, nextState) {
if(this.state === nextState) return false
else return true
}
如果要让子组件不受父组件的影响,通过对props进行对比即可。平时我们是引入Component,改成PureComponent就好,这个组件里面已经帮我们重新写了shouldComponentUpdate这个方法了。
但是PureComponent是有缺点的:
- 只是进行
state和props的数据进行浅比较,如果只是改变了对象内部的一个数据的话,shouldComponentUpdate还是会返回false,阻止render()的进行; - 也就是说,我们不能直接修改
state数据,而是要产生新数据
// this.setState({name: 'mannqo'}) 产生新数据
// 直接修改数据例子如下,这样会修改失败
const obj = this.state; // 此时obj.name = 'aaa'
obj.name = 'mannqo'
console.log(obj === this.state); // true
this.setState(obj);
项目中一般使用PureComponent进行优化
六、render props
向组件内部动态传入带内容的标签,比如:
标签体中的内容可以在this.props.children拿到;
children props
import React, { Component } from 'react'
export default class Parent extends Component {
render() {
return (
<div><A><B /></A></div>
)
}
}
// 在A组件中需要{this.props.children}才能引入B组件
这里Parent是A的父组件,此时如果B组件需要用到A组件内的数据,没办法通过this.props获取到,因为此时的this指向的是Parent而不是A组件;所以在开发中遇到这种场景的时候就要用到render props.
render props:可以用render传递数据,让A中的数据可以让A标签中通过调用render拿到
<A render={(name)=><B name={name}/>}/> // 在parent中
{this.props.render(name)} // A组件中
在A中拿到A中的this.state.name数据进行传递,父组件parent中的A组件调用render就可以拿到从A组件中传递过去的name值,并且将B组件渲染出来;
六、错误边界(error boundary)
只能用于生产环境,也就是要打包之后,这个错误边界才有效。
有时候可能会产生一些未知的错误,我们需要尽可能地让这个错误的影响减小,错误边界就是把错误限制在一定范围内,这个时候我们需要其父组件对这个组件进行限制;
当Parent的子组件出现报错时,会触发getDerivedStateFromError,并且携带错误信息。配合componentDidCatch使用,用于统计页面的错误,发送请求给 后台
state = {hasError: ''} // 标识子组件是否产生错误
export default class Parent extends Component {
static getDerivedStateFromError(error) {
return {hasError: error}
}
componentDidCatch(error, info) {
console.log(error, info);
}
render() {
return (
<div>
<h2>Parent组件</h2>
{this.state.hasError ? <h2>有错误</h2> : <Child/>}
</div>
)
}
}
特点:只能捕获(比如render里面出的错)后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成时间、定时器中产生的错误。