美文网首页
React基础v2

React基础v2

作者: hellomyshadow | 来源:发表于2020-08-27 15:02 被阅读0次

    父子组件通信

    1. props -父组件向子组件传递数据

      • 父组件
        import Header from './Header'  // 引入子组件Header
        this.state = { msg:'123' }
        run= () => {
           // ...
        }
        render() {
            return(<div><Header msg={this.state.msg} /><div>)
        }
        
      • 子组件Header.js
        constructor(props) {  // props 是一个对象,用于接收父组件的数据
            super(props)
        }
        render() {
            return(<div>
                <span>{this.props.msg}<span>
                <button onClick={this.props.run}>Run Test</button>
            <div>)
        }
        
    2. Context API - 跨层级通信,祖代组件向任意层级的后代组件传递
      ps:Vue 中的 provide/inject 来源于Context
      Context模式下有两个角色:Provider - 外层提供数据的组件,Consumer - 内存获取数据的组件。

      • 创建 Context 上下文,放在一个文件中,方便引用
        // storecontext.js
        
        import React from 'react'
        // Context 可以创建多个!
        const StoreContext = React.createContext(/*可以设置默认值*/)
        export default StoreContext
        
        React 渲染一个订阅 Context 对象的组件时,这个组件会从组件树中离自身最近的那个匹配的 Provider 中读取到当前的 context 值;只有当组件所在的树中没有匹配到 Provider 时,设置的默认值才会生效。这有助于在不使用 Provider 包装组件的情况下,对组件进行测试。
      • 在父组件中使用 Provider,它接收一个 value 属性,当 value 值发生变化时,它内部的所有消费组件都会重新渲染
        import StoreContext from './StoreContext'
        
        // 创建一个数据源
        const store = {
            name: 'Tom',
            age: 20
        }
        
        render() {
            return(
                <StoreContext.Provider value={store}>
                    <AAA></AAA>
                </StoreContext.Provider>
            )
        }
        
      • 在后代组件中使用 Consumer,它包裹一个函数,回调的参数正是距离自己最近的 Provider 提供的数据源(value);如果没有找到Provider,则回调 createContext() 的默认值;
        import StoreContext from './StoreContext'
        
        render() {
            return(<StoreContext.Consumer>
                {
                    value => {
                        return(<div>
                                <p>姓名: {value.name}</p>
                                <p>年龄: {value.age}</p>
                            </div>)
                    }
                }
            </StoreContext.Consumer>)
        }
        
        # 作为 props 直接把Consumer的值传递给组件
        render() {
            return(<StoreContext.Consumer>
                { ctx => <TitleBar {...ctx} /> }
            </StoreContext.Consumer>)
        }
        // 函数式组件
        function TitleBar(props) {
            console.log(props)
            return <div>TitleBar</div>
        }
        
      • Class.contextType 实现编程式消费最近Context的值,可以在任何生命周期中访问它
        import StoreContext from './StoreContext'
        class Titlebar extends React.Component {
            componentDidMount() {
                // StoreContext 共享的值被赋予 this.context
                let value = this.context;
            }
            render() {
                let value = this.context;
                // ...
            }
        }
        Titlebar.contextType = StoreContext;
        
    3. refs - 父组件主动获取子组件的数据
      React提供了 refs 管理组件标签上的自定义属性ref

      <Header ref="header" />
      
      this.refs.header  // 访问子组件的数据
      
    4. 发布订阅,第三方包如pubsub-js

    5. defaultPropspropTypes

      1. 父组件在调用子组件时,如果不给子组件传值,则子组件可以使用 defaultProps定义默认值;
        class Header extends React.Component { ... }
        Header.defaultProps = {
            msg: '0'  //如果父组件没有传递msg,则使用此默认值
        }
        export default Header;
        
      2. propTypes:用于验证父组件传值类型的合法性;
        import PropTypes from 'prop-types'  //react的一个内置模块
        Header.propTypes = {
            msg: PropTypes.number   //父组件传递msg的数据类型必须是number
        }
        

    ref

    1. React v16.3引入了 React.createRef() 创建Ref,字符串类型的 Ref 将慢慢被抛弃;
      class MyComponent extends React.Component {
          constructor(props) {
              super(props);
              this.myRef = React.createRef();
          }
          render() {
              return <div ref={this.myRef} />;
          }
      }
      
      1. React 会在组件加载时设置refcurrent 属性,在卸载时则会改回 null
      2. ref 的更新会发生在钩子 componentDidMount()componentDidUpdate() 之前。
    2. 访问
      const node = this.myRef.current;
      
      current 的值取决于节点的类型
      • 普通HTML元素,值为DOM对象
      • React组件,值为实例对象
    3. 函数式组件上不能使用 ref 属性,因为它没有实例,但它的内部可以使用 ref 属性;
    4. React v16.3推荐使用 Ref 转发,从而把子组件的 Ref 暴漏给父组件;
    5. 回调式Ref
      这种设置ref的方式可以更加细致地控制 ref 的设置与解除。
      class CustomTextInput extends React.Component {
          constructor(props) {
              super(props);
              this.textInput = null;
          }
          this.setTextInputRef = element => {
              this.textInput = element;
          }
          render() {
              return <input type="text" ref={this.setTextInputRef} />
          }
      }
      
      • ref 属性接收一个函数,在组件挂载时回调 ref 函数,当卸载时传入 null 并回调;
      • 在组件间传递以回调的形式传递 refs
        class Parent extends React.Component {
            render() {
                return <CustomTextInput inputRef={el => this.inputElement = el} />
            }
        }
        
        function CustomTextInput(props) {
            return <input ref={props.inputRef} />
        }
        

    生命周期

    React V16.3之前的生命周期

    V16.3之前的生命周期 生命周期 生命周期-before
    • this.forceUpdate() 手动强制触发组件的render渲染,会导致组件跳过shouldComponentUpdate(),直接调用render()
      应用场景:有些变量不在 state 上,或者 state 里的某个变量层次太深,更新时没有自动触发render()
    • React V16.0 引入新的生命周期:componentDidCatch(),如果 render() 函数抛出错误,该函数勾子可以捕捉到错误信息,且可以展示相应的错误提示。
      注:它只是一个增量式的修改,完全不影响原有的生命周期勾子。
    1. 组件首次加载过程中触发的函数
      constructor --> componentWillMount --> render --> componentDidMount
      
      • componentWillMount():组件将要挂载
      • render():数据/模板渲染
      • componentDidMount():组件加载完成
      • constructor()render()在组件加载时会被触发,但并不属于生命周期函数
    2. 组件状态更新过程中触发的函数
      shouldComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate
      
      • shouldComponentUpdate():是否更新组件,常用于做组件优化,返回 true 则更新,否则不更新。
         shouldComponentUpdate(nextProps, nextState) {
             // nextProps:表示父组件向当前组件传递的数据
             // nextState:当前组件更新后的数据,只是还没有重新渲染DOM
         }
        
      • componentWillUpdate():将要更新组件;
      • render():更新数据/模板;
      • componentDidUpdate():数据更新完成。
    3. 在父组件中改变 props 传值时触发的函数:componentWillReceiveProps(),然后才会触发组件更新的 render() 函数。
    4. 组件销毁时触发的函数:componentWillUnmount()
      1. 父组件
        this.state = { flag:true }
        setFlag() {
            this.setState({ flag:!this.state.flag })
        }
        render() {
            return(<div>
                { this.state.flag && <Header /> }
                <button onClick={this.setFlag}>挂载与销毁</button>
            </div>)
        }
        
      2. 子组件Header
        componentWillUnmount() {
            console.log('Header被销毁')
        }
        
    5. 手动移除某个容器上的组件:
      ReactDOM.unmountComponentAtNode(containerDom)
      

    React V16.3的新生命周期

    React V16.3 的更新中,除了被热烈讨论的新 Context API 之外,新引入的两个生命周期函数 static getDerivedStateFromProps、getSnapshotBeforeUpdate 以及在未来 V17.0 版本中即将被移除的三个生命周期函数 componentWillMount、componentWillReceiveProps、componentWillUpdate。这三个准备废弃的生命周期用 getDerivedStateFromProps 替代。如果仍想使用废弃的生命周期,可以加上前缀:UNSAFE_,如 UNSAFE_componentWillMount

    React生命周期-after.png

    原来的生命周期在 React v16 推出 Fiber 之后就不合适了,因为如果开启 async rendering,在 render() 之前的所有函数都可能被执行多次。
    render() 之前执行的生命周期:componentWillMount、componentWillReceiveProps、shouldComponentUpdate、componentWillUpdate
    除了 shouldComponentUpdate,其他三个都被静态函数getDerivedStateFromProps取代。
    这种做法强制开发者在 render() 执行之前只做无副作用的操作,而且能做的操作局限在根据 propsstate 决定新的state

    • state getDerivedStateFromProps(props, state) 会在 render() 之前被调用,并且在初始化挂载及后续更新时都会被调用,返回一个对象来更新state,如果返回null 则不执行更新。
    • getSnapshotBeforeUpdate(prevProps, prevState)render() 之后、componentDidUpdate() 之前。使得组件能在发生更改之前从DOM中获取一些信息(如滚动位置)。返回值将作为参数snapshot传递给componentDidUpdate(prevProps, prevState, snapshot)

    错误边界

    默认清空下,若一个组件在渲染期间(render)发生错误,会导致整个组件树全部被卸载。
    错误边界: 是一个组件,它能够捕获到渲染期间子组件发生的错误,并有能力阻止错误继续传播。
    错误边界能捕获在渲染过程中 所有子组件的constructor和生命周期函数内发生的错误。

    错误边界不能捕获的错误类型:

    • 发生在事件处理器里面的
    • 异步代码,如setTimeout、requestAnimationFrame
    • 服务端渲染
    • 自己本身抛出的错误

    在代码层面上,只要一个类组件中定义了 static getDerivedStateFromError() 或者 componentDidCatch(),它就是一个错误边界(组件)。
    一般来说,getDerivedStateFromError()是不允许发生副作用的,故而负责呈现一个备用的Fallback UI给用户;componentDidCatch()允许发生副作用,故负责打印错误日志,上报错误到远程服务器。

    class ErrorBoundary extends React.Component {
        constructor(props) {
            super(props);
            this.state = { hasError: false };
        }
     
        static getDerivedStateFromError(error) {
            // Update state so the next render will show the fallback UI.
            return { hasError: true };
        }
     
        componentDidCatch(error, info) {
            // You can also log the error to an error reporting service
            // error 表示被抛出的错误
            // info 表示一个含有 ```componentStack``` 属性的对象
            logErrorToMyService(error, info);
        }
     
        render() {
            if (this.state.hasError) {
                // You can render any custom fallback UI
                return <h1>Something went wrong.</h1>;
            }
            
            return this.props.children; 
        }
    }
    

    然后可以把它当做一个普通的组件来用:

    <ErrorBoundary>
        <MyWidget />
    </ErrorBoundary>
    

    <ErrorBoundary>使用起来就像try-catch语句,只不过它是用于React组件,且保留了React原生的声明特性。

    注意: <ErrorBoundary>不能捕获自己所产生的错误,只能捕获在它之下的组件树所产生的错误。
    <ErrorBoundary>嵌套使用的情况下,如果某个<ErrorBoundary>不能渲染一些错误信息(调用static getDerivedStateFromError()失败),那么这个错误就会往上冒泡到层级最近的<ErrorBoundary>。这也是try-catch语句在Javascript里的执行机制。

    相关文章

      网友评论

          本文标题:React基础v2

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