美文网首页
react入门三

react入门三

作者: liquan_醴泉 | 来源:发表于2021-03-28 21:40 被阅读0次

    ReactDOM.render(<MyComponent/>, document.getElementById("root"))发生了什么

    react解析MyComponent标签找到了MyComponent组件,发现组件是类定义的,随后new出该类的实例,并通过该实例调用到原型上的render方法
    将render方法返回的虚拟dom转为真实dom,随后呈现在页面中,呈现后还会调用一个componentDidMount

    ref

    • 字符串的形式(不推荐使用,存在性能问题)

    • 回调的方式
      <input ref={(currentNode) => {this.input = currentNode}}/>, 该回调一上来就会调用一次
      PS: 向上面这种回调的方式(内联的方式)在更新的时候会调用两次,注意是更新的时候,第一次currentNode为null(因为每次render都会创建一个新的内联函数,当更新的时候第一次调用是为了清空就得函数,所以返回一个null),第二次才会把节点传入. 如果非得解决这个问题,react也提供了方案,通过类绑定的方式:

      <input ref={this.saveInput}/>,

      在class里面定义saveInput

      saveInput = (c) => { // c就是传入的node节点
      this.input1 = c;
      }

    createRef

    class Demo extends React.Compoent{
        myRef = React.createRef(); // createRef返回一个容器,该容器用于存放被ref所标识的节点,该容器专人专用只能绑定一个,因此后放进去的会覆盖之前的,如果想使用多个就需要createRef多次
        render() {
            return (
                <input ref={this.myRef}/> {/*这里相当于将input存放到了myRef中了,input节点可以通过this.myRef.current访问*/}
            ) 
        }
    }
    
    

    react中的事件

    • 1 通过onXXX属性指定事件处理函数
      react中使用的是自定义事件(合成事件,而不是使用原生的dom事件)--- 是为了更好的兼容性

        react中的事件是通过事件委托的方式处理的(委托给组件最外层元素)--- 事件委托的原理是事件冒泡,使用事件委托高效
      
    • 2 通过event.target得到发生事件的dom元素对象

    收集表单数据

    • 非受控组件

      现用现取,通过ref可以完成

    • 受控组件

      输入类的元素在随着输入将值维护到状态中

    高阶函数和柯里化

    高阶函数:
    若函数A接受的参数是一个函数,那么A就称之为高阶函数
    若函数A调用的返回值依然是一个函数,那么A就称之为高阶函数
    常见的高阶函数:如Promise, setTimeout, 数组的常用方式
    函数柯里化
    部分求值,函数的调用仍旧返回函数,实现多次接受参数最后统一处理的函数编码

    生命周期

    组件从创建到死亡会经历一些特定的阶段

    React组件中包含一系列钩子函数,会在特定的时刻调用

    在定义组件时,会在特定的声明周期回调函数中做特定的工作

    • 旧版的生命周期钩子

    组件挂载的执行顺序
    constructor ---> componentWillMount ---> render ---> componentDidMount ---> componentWillUnmount

    组件更新执行顺序(可以分为3条线)

    父组件渲染子组件,当传入的数据更新时的执行路线

    componentWillReceiveProps ---> shouldComponentUpdate ---> componenWillUpdate ---> render ---> componentDidUpdate ---> componentWillUnmount

    当组件setState更新的时候

    setState() ---> shouldComponentUpdate ---> componentWillUpdate ---> render ---> componentDidUpdate ---> componentWillUnmount

    当强制更新组件的时候(不更改状态数据就想让组件更新就可以走这条线)

    forceUpdate() ---> componentWillUpdate ---> render ---> componentDidUpdate ---> componentWillUnmount

    ps: shouldComponentUpdate是一个阀门钩子,如果不写这个钩子默认返回true,接下来的流程正常执行,如果写了并且返回false则接下来的流程不会执行,只能返回true或者false,

    componentWillReciveProps 这个钩子第一次不调用,只有接受的props变化了才会调用,也就是父组件重新渲染导致子组件渲染才执行该钩子,接受新的props。
    

    总结:

    初始化阶段
    constructor ---> componentWillMount ---> render() ---> componentDidMount
    更新阶段: 由组件内部this.setState() 或者父组件更新触发
    shouldComponentUpdate ---> componentWillUpdate ---> render ---> componentDidUpdate

    卸载阶段:由reactDOM.unmountComponentAtNode(node)触发
    componentWillUnmount()

    常用的钩子:
    componentDidMount: 一般做初始化操作例如开启定时器,发送网络请求,订阅消息。。。

    componentWillUnmount: 做一些收尾工作如取消定时器,取消订阅等

    render: 必须用,需要掉1+n次

    • 新的声明周期 (react最新版本)

    所有带will的钩子在新版版中都加了UNSAFE_前缀, 除了卸载的钩子,为什么这么做,因为react在设计异步渲染,这些组件可能会出现bug,以后可能会被废弃

    新版本废弃了componentWillMount, componentWillUpdate, componentWillReciveProps,新增了getDerivedStateFromProps 和getSnapshotBeforeUpdate

    需要注意的是 getDerivedStateFromProps不能在实例上调用,需要声明成静态的需要加static, 而且该方法必须要有返回值,要么你返回状态对象,要么返回null,不能返回其他的。需要注意的是只要返回一个对象,那么状态的更新就没有意义了,因为一旦返回状态,此状态就不能被改了,该方法接收一个参数props, 他会接收props属性并且会派生出一个取决于props的状态。可以接收第二个参数state,使用场景罕见,基本不用。

    getSnapshotBeforeUpdate: 该钩子处于render和componentDidUpdate之间,用于在更新之前获取快照,因为更新后之前的状态就不见了,在更新之前再看一眼之前的状态。

    componentDidUpdate() 该钩子会接收三个参数,第一个是更新前的props,第二个是更新前的state,第三个是一个快照值(即getSnapshotBeforeUpdate返回的值。)
    挂载时:

    constructor ---> getDerivedStateFromProps ---> render (react更新dom和refs) ---> componentDidMount

    更新:

    (new props | setState() | forceUpdate)时 ---> getDerivedStateFromProps(得到一个派生的状态) ---> shouldComponentUpdate

    dom的diff算法

    class Person extends React.Component{
       state = {
           persons: [
               {id: 2, name: '小张', age: 23},
               {id: 3, name: '小李', age: 19}
           ]
       }
    
       render() {
           return (
               <ul>
                   {
                       this.state.persons.map((person,index) => {
                           return <li key={index}>{person.name}</li>
                       })
                   }
               </ul>
           )
       }
    }
    
    
    

    虚拟dom中的key有什么作用?

    简单说key是虚拟dom对象的标识,在更新显示是key起着极其重要的作用,,
    
    详细说,当状态中的数据发生变化的时候,react会根据新的数据生成新的虚拟dom,随后react进行新虚拟dom和就虚拟dom的diff对比,比较规则如下:
    - 就虚拟dom中找到了与新虚拟dom中相同的key:
        1) 若虚拟dom中内容没有变化,直接使用之前的真实dom
        2) 若虚拟dom中内容变了,则生成新的真实dom,随后替换页面中之前的真实dom
    
    
    - 旧虚拟dom中没有找到与新虚拟dom相同的key
    
        根据数据创建新的真实dom,随后渲染到页面。
    

    用index作为key可能会引发的问题:

    - 若对数据进行逆序添加,逆序删除等破坏顺序操作,会产生没有必要的真实DOM更新 ==》 界面效果没问题,但是效率低
    
    - 如果页面中还包含输入类的dom:
        会产生错误dom更新==》 界面有问题
    
    注意,如果不存在对数据逆序添加删除等破坏顺序的操作,仅用于渲染列表,展示列表,使用index是没有问题的
    

    <input type="checkbox"/> 这样的输入内容在react中有一个defaultChecked属性,可以代替checked属性,因为使用checked,则必须使用onchange来修改其值

    react组件通讯

    • 父子组件 通过props可以通信

    • 兄弟组件 可以提取共同状态到公共父组件或者使用消息的发布订阅,或者使用redux之类的状态管理库

    消息的发布订阅实现兄弟组件之间的通信常用的三方库有pubsubjs

    react路由

    • 前端路由的工作原理

      依托的是浏览器历史记录,可以借助history这个库来完成操作

    • react-router-dom

      路由组件
      会自动给组件传入路由相关的props
      NavLink可以实现路由的高亮,通过activeClassName指定样式名
      标签体内容是一个特殊的标签属性
      通过this.props.children可以获取标签体内容
      路由组件一般放在pages目录
      一般组件
      一般放在components目录

      • Switch组件

        该组件会提高性能,如果不适用他,那么在匹配到目标组件后还会继续往下匹配,使用了他,当匹配到目标组件后就不会网下匹配其他组件了

      • 解决多级路径页面刷新样式丢失的问题

        • public/index.html中引入样式时不写./而是写/
        • public/index.html中引入样式时不写./ 而是%PUBLIC_URL%
        • 不要使用HistoryRouter而是使用HashRouter
      • 路由的严格模式与模糊匹配
        不是非必要情况下不要用严格模式,比如有二级路由的情况下使用了严格模式就会出问题,如/home /home/mian 当使用了严格模式, 那么/home/main永远匹配不到

      • Redirect的使用

        Redirect放在注册路由的最后,当所有路由没有匹配的时候提供一个默认的路由

      • 嵌套路由

        • 注册子路由的时候要写上父路由的path
        • 路由的匹配是按照路由的注册顺序进行的
      • 向路由组件传递params

      路由导航向路由组件传递params参数
      <Link to={`home/message/detail/${id}`}>
      // 可以传递多个参数
      <Link to={`home/message/detail/${id}/${title}`}> // 相当于传递了一个id,一个title
      注册的路由
      
      <Route path="/home/message/detail/:id" component={Detail}>
       // 可以接收多个参数
      <Route path="/home/message/detail/:id/:title" component={Detail}>
      Detail组件通过props参数就能接收到参数,在接收到的match里面就有传递的参数
      
      
    - 向路由组件传递search参数
    
    ```
    <Link to={`home/message/detail/?id=${id}&title=${title}`}> // 相当于传递了一个id,一个title
    
    接受的方式(无需声明正常注册路由即可)
    <Route path="/home/message/detail" component={Detail}>
    
    Detail组件通过this.props.location.search获取,获取到的是一个字符串,我们需要的是对象的格式,因此需要特殊处理,安装脚手架的时候其实下载了一个叫做querystring的库,通过这个库可以帮我们完成
    import qs from 'querystring';
    
    // 通过qs.stringify(obj) 可以将一个对象转为key=value&key=value的格式
    // 通过qs.parse(str) 可以将一个key-value格式的字符串变成对象
    
    ```
    
    • 向路由组件传递state参数 (这里的state不是路由状态的state)

      无论是params参数还是search参数都在地址栏上,还可以通过对象的方式去传递,此时的to属性只能是一个对象,不再是字符串了,
      需要注意的是这种方式传递参数虽然地址栏上没有参数,但是刷新的时候同样不会丢失参数,因为他是存在history中的,如果是HashRouter刷新会丢失

    ```
    <Link to={{pathname:`/home/message/detail`, state: {id: id, title: title}}}>
    
    // 接受state
    通过this.props.location.state可以获取
    
    ```
    
    - push 与 replace
    
    历史记录是一个压栈模式,默认是push,如果要开启replace需要这么写, replace是替换模式
    <Link replace to={{pathname:`/home/message/detail`, state: {id: id, title: title}}}>
    
    - 编程时路由导航
    
    通过脚本的方式进行跳转。路由组件通过this.props.history可以拿到history对象,里面包含操作历史的方法,需要注意的是编程时路由跳转时注册路由需要对应,也就是说通过search跳转的不能以params的方式注册路由,其他同理,params和query的参数在地址栏好理解,state的不再地址栏,但是同理对应的方式也有第二个参数可以接收state, 如this.props.history.push(path, state)
    
    编程时路由导航的几个api:
    * this.props.history.push
    * this.props.history.replace
    * this.props.history.goBack
    * this.props.history.goForward
    * this.props.history.goBack
    * this.props.history.go
    
    - withRouter
    
    路由组件里面可以获取到操作历史的方法,但是再非路由组件里面是没有这些的。可以借助withRouter使一般组件也能获取到history
    import {withRouter} from 'react-router-dom'
    
    比如
    export default withRouter(Header) ; 通过withRouter将一般组件Header加工后暴露,此时使用header组件就会有history相关的内容
    
    
    - BrowserRouter与HashRouter的区别
    
     * 底层原理不一样,BrowserRouter使用的是H5的history API,不兼容Ie9及以下版本HashRouter使用的是url的hash值
     * url表现形式不一样,HashRouter带#
     * 刷新后对路由state的影响
        BrowserRouter没有影响,因为state保存在history对象中
        HashRouter会丢失state参数
    
    * HashRouter可以用于解决一些路径错误相关的问题
    

    redux

    • redux是什么

      • 1.1 redux是专门做状态管理的库不是react插件
      • 1.2 可以用在react,vue,angular中,但基本与react搭配使用
      • 1.3 专门做集中式状态管理,管理应用中多个组件共享的状态
    • 什么情况下用redux

      • 1.1 某个组件的状态需要让其他组件随时拿到(共享)
      • 1.2 一个组件需要改变另一个组件的状态(通信)
    • redux的工作流程

      有一个核心store,里面的值只能通过reducer更改,当用户在组件中派发一个action的时候。action不是特殊的东西,他是一个带有type和数据的对象比如{type: "INCREAMENT", data: {count: 1}}就是一个action,
      通过dispatch可以将action交给store,store不干活,他会让reducer去更改状态。reduer将状态加工完毕会返回一个新的状态给store。reducer能加工状态,那个状态哪里来?其实reducer干了两件事,一是初始化状态,二是加工状态。action creator是一个返回action对象的东西。

    • redux的三个核心概念

      • 1 action 是一个动作对象包含两个属性

        • type 标识属性,值为字符串,唯一,必要属性
        • data 数据属性,值类型任意,可选属性

        action分同步action(一般对象) 和异步action(是一个函数),一个异步的action一定对应一个同步的action,使用异步action需要一个中间件,因为action需要的是一个对象,而不是一个函数,因此需要安装redux-thunk,引入后 需要在创建store的时候作为第二个参数传递,异步的action是通过store帮助调用的

        import {createStore, applyMiddleware} from 'redux'
        import thunk from 'redux-thunk'
        
        createStroe(reducers, applyMiddleware(thunk)) 这里的reducers是个多个reducer,以对象的方式去组织,因为对象的key-value存值和取值更方便。因此reducers的结构应该是这样,是多个reducer合并的结果
        
        首先引入一个函数将所有的reducer合并为一个总的reducer
        import {combineReducers} from 'redux'
        const allReducers = combineReducers({
            countRecuder,
            otherReducer,
            ...
        });
        
      • 2 reducer 用于初始化状态,加工状态,加工时根据旧的state和action,产生新的state的纯函数

          该函数接受两个参数,第一个是之前的状态,第二个是action对象,初始化的时候第一个参数是undefined,在创建store的时候会传递reducer进去,内部会调用reducer进行状态初始化
        
          reducer必须是纯函数,纯函数,一个函数只要接受同样的实参,那么一定得到同样的结果,不能改写参数的数据
        
          ```
          比如:
          let arr = [1,2,3];
          arr.push(4); 虽然arr的内容变了,但是arr的地址引用没有变,这样的变化redux是不认的。这样写才可以
        
          [4, ...arr];这样得到一个新数组 
        
          像这样的函数不是纯函数,同样的输入不能得到同样的输出
          function demo(a) {
              return Math.random() + a
          }
        
          function demo1(a) { // 改写了参数的值
              a = 9
          }
        
          纯函数不会产生任何副作用,例如网络请求,输入和输出设备,不能调用Date.now(), Math.random()等不纯的方法
          ```
        
      • 3 store 将state,action, reducer联系到一起的对象

        通过store.getState() 可以得到状态

        通过store.dispatch(action对象) 可以更改状态,注意不是直接更改,其实每一个action对应一个reducer,最终是通过reducer来更改,需要注意的是redux只是在维护状态,在react中派发action导致转台改变,是不能引起页面的渲染的。因为redux只承诺维护状态,没承诺渲染页面,如果要渲染需要我们检测状态,手动去调用render. 注意手动调用不是this.render. 可以通过this.setState({}) 来重新渲染

        store.subscribe() 可以检测redux的状态,只要redux中的任意状态发生改变,指定的回调都会执行,因此页面一挂载就可以检测redux的状态了

        componentDidMount() {
        store.subscribe(() => {
        this.setState({})
        })
        }

        在组件中写可能写很多这样重复的代码,因此可以将检测的逻辑放在入口的index.js中,这样只要redux的状态一发生变化,真个App就会重新渲染,对应的子组件也会渲染。因为有diff算法的存在,不会引起大面积的重绘重排,因此不用担心性能问题。

    react-redux

    react-redux需要注意的几个点:

    • 所有的ui组件都应该包裹一个容器组件,他们是父子关系
    • 容器组件是真正和redux打交道的,里面可以随意使用redux的api
    • ui组件中不能使用任何redux的api
    • 容器组件会传给ui组件redux中所保存的状态,用于操作状态的方法。
    • 容器给ui传递状态,操作状态的方法均通过props传递
    • redux的状态变化并不会引起ui的更新,除非手动去检测状态变化。但是用了react-redux就不需要检测也能更新ui, 因为使用connect的时候他已经帮我们检测了。

    容器组件就是一个桥梁,用于连接ui组件和redux,因此在容器组件中需要引入要包装的ui组件,要引入store(redux的核心就是store),需要注意的是容器组件不能亲自引入store,而是通过props的方式传入store,比如有一个容器组件Count,此时可以通过props传递
    <Count store={store}/>
    需要注意的是页面中不止这么一个容器组件,他都需要store,那么react-redux提供了一个批量提供store的方法
    import {Provider} from 'react-redux';
    ReactDOM.render(
    <Provider store={store}>
    <App/>
    </Provider>,
    document.getElementById('root')
    )

    需要引入连接的方式
    import {connect} from 'react-redux'
    // 得到一个容器组件
    const ContainerComponent = connect()(ui组件);

    容器组件和父子组件通信通过props的方式传递状态和操作状态的方法,正常的组件是通过标签属性的方式给子组件传递参数的,但是容器组件是由connect方法形成的,并没有和ui组件有这明确的父子关系,因此需要注意的是connect的第一个()需要两个参数。第一个参数是一个函数,用于生成传给ui组件的状态,需要返回一个对象,既然是操作状态,那么该函数一定能接受store中的状态,该函数接受一个参数state,第二个参数同样是一个函数,(当然也可以是一个对象,里面是一系列action,是精简版的写法,这这种写法,react-redux会自动分发对应的action),接受一个分发action的dispatch方法,返回一个对象,对象封装的是操作状态的方法。从语义上来讲,第一个参数就是一个mapStateToProps,第二个参数就是mapDispatchToProps

    示例代码

    funciton mapStateToProps(state) {
        return {
            count: state
        }
    }
    
    function mapDispatchToProps(dispatch) {
        return {
            increament: (number) =>  dispatch(createInreamentAction(number))
        }
    }
    
    export default connect(mapStateToProps, mapDispatchToProps)(ui组件)
    
    mapStateToProps映射状态 mapDispatchToProps映射操作状态的对象, 这两个函数都返回一个对象
    mapDispatchToProps 也可以是一个对象,对象是一系列action,react-redux会自动分发这个action
    

    注意事项:当一个ui组件对应一个容器组件的时候,如果这样编码会导致组件的数量成倍增加,可以同过一个文件将其整合,定义ui组件,最终以容器组件暴露

    redux的开发工具

    一个项目一般都由多个人写,可能都会用到redux存数据,但是彼此之间不知道状态是如何存的,都存了哪些状态,这时可以通过redux的开发工具来快速追踪状态的变化。需要引入一个库redux-devtools-extension
    在store.js中引入

    import {composeWithDevTools} from redux-devtools-extension
    
    export default createStore(allReducers,composeWithDevTools(applyMiddleware(thunk)))
    
    
    

    关于setState

    setState更新状态的两种写法

    • setState(stateChange, [callbacnk]); // 对象式的写法,更改状态需要手动获取原来的state
      eg: this.setState({count: this.state.count+1})
    • setState(updater, [callback]); // 函数式的写法,不需要获取原来的state,因为会作为参数传入
      eg: this.setState((state, props) => { // 不仅能拿到state, 还可以拿到props
      return {count: state.count+1}
      })
      两种写法都有个可选的回调,因为react是异步更细的。需要注意的是setState是一个同步方法,由setState引起react更新状态是一个异步的操作,这个回调在状态更新完成并且在render后才调用的.

    lazyLoad

    路由组件的懒加载,不适用懒加载,所有的路由组件都会一次性被加载,如果有成百上千个路由组件,这是非常恐怖的。路由组件应该在跳那个路由就加载那个路由组件,这就是懒加载,在需要的时候才加载组件
    

    hooks

    • React.useState(状态初始值)

      该方法返回一个数组,数组第一位是当前状态,第二位是更新状态的函数,因为数组的解构赋值是按下标来的,所以如下:

      let [count, setCount] = React.useState(0);

      需要注意的是每次调用setCount更新,函数组件都会执行,setCount的参数可以是一个函数,

    • React.useEffect(fn,[arr]) 可以在函数式组件里模拟生命周期钩子,fn就相当于一个声明周期钩子至于是哪一个需要看情况

      如果不指定第二个参数arr,那么所有的状态的改变都会执行回调fn。如果arr是空,表示全都不监测,如果不写第二个参数那么表示全部进行监测,如果要监听指定的状态,就需要在数组中指明。fn可以返回一个函数。

    • React.useRef()
      和createRef一样的作用

    • React.createContext

      1. 创建context容器对象
        const xxxContext = React.createContext();

      2. 渲染子组件时,外面包裹xxxContext.Provider,通过value属性给后代组件传递数据
        <xxxContext.Provider value={数据}>
        子组件
        </xxxContext.Provider>

      3)后代组件读取数据
      方式一,仅适用于类组件
      首先声明接收 static contextType = xxxContext 然后才能通过this.context.value拿到

        方式二,函数式组件和类组件都能接收到
        <xxxContext.Consumer>
            {
      
                value => ( // value就是context中的value数据
                    要显示的内容
                )
            }
      
        </xxxContext.Consumer>
      

      注意,在应用开发中一般不用context,一般都用它来封装react插件。

    组件优化

    - Component的两个问题
        1) 只要执行setState(), 即使不改变状态数据,组件也会重新render() ,也就是setState({}) 也会render.
        2) 只要当前组件重新render,就会自动重新render子组件 ,效率低。
    
      
    
        高效的做法应该是
            只有当前组件的state或者props数据发生改变时才重新render
    
        之所以有这样的问题是因为Component中的shouldComponentUpdate()默认总是返回true. 通过重写这个钩子可以解决这个问题
    
        shouldComponentUpdate(nextProps, nextState){
            
            console.log(this.props, this.state) // 当前的props和state
            console.log(nextProps, nextState); // 目标props和state
            // 根据当前数据和目标数据对比来决定返回ture|false ,至于怎么比有人做了,react中提供了PureComponet这个组件,他会重写 shouldComponentUpdate, 里面的对比逻辑是写好了的
            reuturn true;
        }
    
    - PureComponent 
    
        该组件其实也没干啥,就是重写了shouldComponentUpdate, PureComponent也会有一点小瑕疵。因为他的底层进行了浅对比,因此如果是下面的这种写法同样会有问题
    
        const obj = this.state;
        obj.xxx = 'XXX'; // 改了某一个属性
        this.setState(obj); // 因为obj和state指向了同一个引用,因此在使用PureComponent比较时认为state是没有变化的,所以不会render.因此一定注意使用PureComponent,在更新状态时不要和原来的状态发生任何关系。使用时额外注意数组的那些方法。
    

    renderProps

    场景,比如有两个组件A,B,不应该关系不大,但是因为某些逻辑的原因可能需要这么写,
    
    <A>
        <B/>
    </A>
    对于这种格式要个可以通过this.props.children来获取,但是有个问题如果B组件需要A组件内的数据是做不到的,因此就有了下面的这种格式
    
    <A render={(parmas) => <B {...params}/>}/> // 就相当于传递了一个函数,该函数返回一个组件,函数名不一定叫render.
    
    在A组件中预留一个位置this.props.render(可以传递A中的状态到B),类似于vue中插槽,
    

    组件通信的总结

    * 组件间的关系
    
        1) 父子组件
        2) 兄弟组件 (非嵌套组件)
        3) 祖孙组件(跨级组件)
    
    * 几种通信方式
    
        1) props
            1.1) children props
            1.2) render props
    
        2) 消息的发布订阅
                pubsub ,event等
        3) 集中式管理如redux, dva等
    
        4) context:
                生产者和消费者模式
    

    快速预览打包后的程序

    全局安装serve,会快速开启一个服务。

    错误边界

    一个页面有多个组件构成,但是其中的某个组件因为某些不可控因素导致出错,此时整个页面都会出问题,边界错误就是处理这类问题的,就是当某个组件发生了错误,不要导致整个页面崩溃,而是将错误控制在最小的范围了,出错了给一个友好的提示。需要注意的是错误边界只有在生产环境有效,就是打包后才生效,开发环境是不生效的。总的来说错误边界就是用来捕获后代组件错误,渲染出备用页面,只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件,定时器中产生的错误。
    
    当Parent的子组件出错时会走这个钩子,定义在父组件,并且携带错误信息err
    state = {
        error: '' // 默认空,当发生了错误就会有值,可以根据这个值给一个友好的提示
    }
    static getDerivedFromError(err) {
        return {error: err}
    }
    
    当然还有一个钩子componentDidCatch() {
        console.log('渲染组件出错');
    } 当组件发生错误的时候就会触发该钩子,这里可以统计发生错误的次数

    相关文章

      网友评论

          本文标题:react入门三

          本文链接:https://www.haomeiwen.com/subject/fdpmhltx.html