React扩展

282 阅读6分钟

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

React扩展

setState、lazyLoad、Context、组件优化、PureComponent、错误边界的使用

一、setState

对象式的setState是函数式的setState的语法糖,如果新状态依赖于原状态就使用对象方式,如果依赖就使用函数方式(比如下面这个例子,把count改成count+1);

  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

  2. 函数式的setState:

    this.setState(state => ({ count: state.count + 1 }));
    

    函数式的setState的第一个参数就是一个updater函数,可以接收到stateprops,因此不需要提前从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,使用步骤如下:

  1. 创建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存在的问题

  1. 只要执行setState,即使不改变状态数据,组件也会重新render()
  2. 只要当前组件重新render(),即使子组件没有用到父组件的任何数据,还是会自动重新render子组件

这两种情况都会导致效率低,都是由于Component中的shouldComponentUpdate()总是返回true。如果我们想要提高效率,就要让它只要当组件的state或者props数据发生改变的时候再重新render(),要解决这个问题,有两个方法:

重写shouldComponentUpdate()方法(PureComponent)

这个方法可以拿到即将要变化的propsstate,在这个时候,我们可以将即将变化的propsstate的和当前的propsstate进行比较,如果是一样就返回false,进而阻止render()的进行。

shouldComponentUpdate(nextProps, nextState) {
    if(this.state === nextState)  return  false
    else return true
}

如果要让子组件不受父组件的影响,通过对props进行对比即可。平时我们是引入Component,改成PureComponent就好,这个组件里面已经帮我们重新写了shouldComponentUpdate这个方法了。

但是PureComponent是有缺点的:

  • 只是进行stateprops的数据进行浅比较,如果只是改变了对象内部的一个数据的话,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里面出的错)后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成时间、定时器中产生的错误。