react

1.PropTypes: 检查组件属性类型

import PropTypes from 'prop-types';

Xxxx.propTypes = { name: PropTypes.string };

当你传给组件的属性不符合验证类型,控制台会打印警告, 处于性能原因,propTypes只在开发模式下进行检查.  

import PropTypes from 'prop-types';

MyComponent.propTypes = {
  // 你可以将属性声明为以下 JS 原生类型
  optionalArray: PropTypes.array,
  optionalBool: PropTypes.bool,
  optionalFunc: PropTypes.func,
  optionalNumber: PropTypes.number,
  optionalObject: PropTypes.object,
  optionalString: PropTypes.string,
  optionalSymbol: PropTypes.symbol,

  // 任何可被渲染的元素(包括数字、字符串、子元素或数组)。
  optionalNode: PropTypes.node,

  // 一个 React 元素
  optionalElement: PropTypes.element,

  // 你也可以声明属性为某个类的实例,这里使用 JS 的
  // instanceof 操作符实现。
  optionalMessage: PropTypes.instanceOf(Message),

  // 你也可以限制你的属性值是某个特定值之一
  optionalEnum: PropTypes.oneOf(['News', 'Photos']),

  // 限制它为列举类型之一的对象
  optionalUnion: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.number,
    PropTypes.instanceOf(Message)
  ]),

  // 一个指定元素类型的数组
  optionalArrayOf: PropTypes.arrayOf(PropTypes.number),

  // 一个指定类型的对象
  optionalObjectOf: PropTypes.objectOf(PropTypes.number),

  // 一个指定属性及其类型的对象
  optionalObjectWithShape: PropTypes.shape({
    color: PropTypes.string,
    fontSize: PropTypes.number
  }),

  // 你也可以在任何 PropTypes 属性后面加上 `isRequired` 
  // 后缀,这样如果这个属性父组件没有提供时,会打印警告信息
  requiredFunc: PropTypes.func.isRequired,

  // 任意类型的数据
  requiredAny: PropTypes.any.isRequired,

  // 你也可以指定一个自定义验证器。它应该在验证失败时返回
  // 一个 Error 对象而不是 `console.warn` 或抛出异常。
  // 不过在 `oneOfType` 中它不起作用。
  customProp: function(props, propName, componentName) {
    if (!/matchme/.test(props[propName])) {
      return new Error(
        'Invalid prop `' + propName + '` supplied to' +
        ' `' + componentName + '`. Validation failed.'
      );
    }
  },

  // 不过你可以提供一个自定义的 `arrayOf` 或 `objectOf` 
  // 验证器,它应该在验证失败时返回一个 Error 对象。 它被用
  // 于验证数组或对象的每个值。验证器前两个参数的第一个是数组
  // 或对象本身,第二个是它们对应的键。
  customArrayProp: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
    if (!/matchme/.test(propValue[key])) {
      return new Error(
        'Invalid prop `' + propFullName + '` supplied to' +
        ' `' + componentName + '`. Validation failed.'
      );
    }
  })
};

限制单个子代:指定只传递一个子代

import PropTypes from 'prop-types';

class MyComponent extends React.Component {
  render() {
    // This must be exactly one element or it will warn.
    const children = this.props.children;
    return (
      <div>
        {children}
      </div>
    );
  }
}

MyComponent.propTypes = {
  children: PropTypes.element.isRequired
};

属性默认值:  Xxx.defaultProps = { name: 'Stranger' }; 先defaultProps后propTypes检查.

2.Refs & DOM

何时使用Refs: 1.处理焦点、文本选择或媒体控制. 2.触发强制动画. 3.集成第三方DOM库

v16.3后使用: <div ref={this.myRef} /> 在constructor中: this.myRef = React.createRef();

ref的值取决于节点的类型:

  • 当ref值被用于一个普通的html元素时, React.createRef()将接受底层DOM元素作为它的current属性以创建ref. React会在组件加载时将DOM元素传入current属性,在卸载时则会改回null.
  • 当ref属性被用于一个自定义类组件时, ref对象将接受该组件已挂载的实例作为它的current. 函数式组件没有实例不能用ref属性, 但可以在函数式组件内指向dom或class组件, 此时写法: 提前声明一个变量let textInput = null;  然后return中 <input  ref={input => { textInput = input; }}/>
  • ref的更新会在componentDidMount或componentDidUpdate之前

对父组件暴露DOM节点

  1. ref转发:
    • 在最上层父亲类的构造函数中使用this.myRef=React.createRef();创建一个ref,
    • 通过ref={this.myRef}传递给子组件, 子组件使用React.forwardRef返回,可以用函数式组件或高阶组件 
      //函数式组件
      const Child=React.forwardRef((props, ref)=>(
          <input ref={ref} />
      ));
      
      //高阶组件
      return React.forwardRef((props, ref) =>(
          <LogProps {...props} forwardedRef={ref} />
      ));
      render() {
          const {forwardedRef, ...rest} = this.props;
      
          // Assign the custom prop "forwardedRef" as a ref
          return <Component ref={forwardedRef} {...rest} />;
      }
    • 在父亲类中使用ref
  2. 将 ref 作为特殊名字的 prop 直接传递:
    • 在最上层父亲类的构造函数中使用this.myRef=React.createRef(); 或者在子组件中使用回调refs创建一个ref
    • 通过xxx(不为ref的props)传递给子组件inputRef={this.myRef}
    • 在自组件中 : ref = {props.inputRef}
    • 此方法可传递多层

回调refs:

<input type="text" ref={this.setTextInputRef} />  this.setTextInputRef = element => {this.textInput = element;}

3.性能优化

前提: 测性能的时候使用生产版本, 可以用chrom的react开发者工具, 生产模式是黑色, 开发模式是红色.

Chrome浏览器内看性能:

  1. 在项目地址栏内添加查询字符串 ?react_perf(例如, http://localhost:3000/?react_perf)。

  2. 打开Chrome开发工具Performance 标签页点击Record.

  3. 执行你想要分析的动作。不要记录超过20s,不然Chrome可能会挂起。

  4. 停止记录。

  5. React事件将会被归类在 User Timing标签下。

虚拟化长列表: 类似无限循环的yo-list

避免协调: 使用shouldComponentUpdate(nextProps, nextState)比较this.props/this.state与nextProps/nextState或使用React.PureComponent ,大部分时候使用pureComponent则不用写scu

关于PureComponent, 之前看的:

React创建了PureComponent组件创建了默认的shouldComponentUpdate行为。这个默认的shouldComponentUpdate行为会一一比较props和state中所有的属性,只有当其中任意一项发生改变是,才会进行重绘。用以取代PureRenderMixin,与PureRenderMixin区别:PureComponent没有实现 shouldComponentUpdate。是在 ReactCompositeComponen中做的props/state的比对。

不会突变的数据的力量:

  • 问题:   
    const words = this.state.words;
    words.push('marklar');
    this.setState({words: words});
    //因为words是一个对象,因此shouldComponentUpdate会认为数据没有改变
  • 突变就是数据创建后,在另一时间点改变
  • 个人理解这里不该说不会突变,因为正常对象都是会突变的, 应该说不要突变数据: 每当我们想要改变state的时候, 使用解构或concat进行一层浅拷贝, 这样就不会突变原始对象, 而是使用一个新对象, 只不过这个新对象和旧对象的下一层都指向相同的数据(浅拷贝, 只拷贝一层, 第二层以后都相同)

使用不可突变的数据结构: 

Immutable.js是解决这个问题的另一种方法. 它通过结构共享提供不可突变的, 持久的集合:

  • 不可突变: 一旦创建, 集合就不能在另一时间点改变 。 不可突变性使得跟踪改变很方便。每个变化都会导致产生一个新的对象,因此我们只需要检查对象的引用是否改变即可。
  • 持久性: 可以使用原始集合和一个突变来创建新的集合,原始集合在新的集合创建后仍然可用
  • 结构共享: 新集合尽可能多的使用原始集合的结构来创建, 以便将复制操作降到最少从而提升性能
const SomeRecord = Immutable.Record({ foo: null });
const x = new SomeRecord({ foo: 'bar' });
const y = x.set('foo', 'baz');
x === y; // false

关于immutable,之前看的:

了解immutable:
    react数据驱动节点重新渲染:只要setState就会重新渲染(看到有的博文说这种说法是错误的,setState后会放在一个队列中,在事件完成时对队列进行结算,此说法有待考证,即便为真也不影响后面的需求),即使数据完全没有变或者改变的数据并不在渲染的节点内使用。 ->  解决使用shouldComponentUpdate比较前后state变化,确定是否应该重新渲染。一种工具是PureRenderMixin,但是!(人生就是这么多转折哈)
    PureRenderMixin:This only shallowly compares the objects. If these contain complex data structures, it may produce false-negatives for deeper differences. Only mix into components which have simple props and state, or use forceUpdate() when you know deep data structures have changed. Or, consider using immutable objects to facilitate fast comparisons of nested data.  —>  是浅拷贝,不能使用于多层的比较,但如果自己deepClone再比较会复杂代码,降低性能。因此引入immutable.js。
    immutable的原理是: Persistent Data Structure(持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变(类似deepclone)。    使用Structural Sharing(结构共享),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享,尽量复用内存,没有被引用的对象也会垃圾回收掉。还加了很多类似c++ STL的数据结构。
    因此:用Immutable.js将state中的数据包装一下,不需深拷贝就可以直接修改。由于修改后返回的是新对象,React.js只需要在oldState.obj === newState.obj这一层就能判断出obj产生了变化

剩余疑问: 猜测immutable是通过结构共享实现了高效的类似深拷贝的效果,是怎么共享的,只有最后的叶子结点共享?

 

4. diff算法

  • 正常将一棵树转换成另一棵树的时间复杂度为O(n3)。react基于两点假设实现了一个O(n)算法:
  1. 两个不同类型的元素将产生不同的树
  2. 开发者可以使用key属性来提示哪些子元素在多次不同的渲染中是稳定的
  •  diff算法的几条原则

  1. 首先比较新旧两棵树的根元素:

    1. 不同类型DOM或组件元素:将拆除旧树并且从0开始重新构建新树,这个根下任何组件也都将被卸载,状态也都被销毁
    2. 相同类型的DOM元素:观察二者的attributes,保持相同的底层DOM节点,并仅更新变化的属性。
    3. 相同类型的组件元素:当组件更新时,实例保持相同,因此在render的过程中state会被为维护好。react更新底层组件实例的props来匹配新元素,在底层实例中调用componentWillReceiveProps()和componentWillUpdate(); 然后,调用render方法并且diff算法递归处理之前的结果和新的结果
  2. 子代上的递归 :react同时迭代两个列表的子元素并在出现不同的时候产生一个变更。
    1. 例如
      <ul>
        <li>first</li>
        <li>second</li>
      </ul>
      
      <ul>
        <li>first</li>
        <li>second</li>
        <li>third</li>
      </ul>
      //react将匹配两棵树的<li>first</li>,匹配两棵树的<li>second</li>,然后在树中插入<li>third</li>
    2. 但如果你的实现方式过于天真,在开头插入元素会使性能变坏。
      <ul>
        <li>Duke</li>
        <li>Villanova</li>
      </ul>
      
      <ul>
        <li>Connecticut</li>
        <li>Duke</li>
        <li>Villanova</li>
      </ul>
      //react将修改每个子节点,而非意识到可以完整保留<li>Duke</li> 和 <li>Villanova</li>子树
  3. keys : 为了解决上述问题(元素没变,顺序变了): react支持key属性。当子节点有key时,React使用key来匹配旧树的子节点和新树的子节点。key值可以是不会重复的id,如果id可能重复则需自己维护一个哈希值作为key,key必须在兄弟中唯一。
  4. 权衡 : react会在每次action后rerender整个app,最后的结果可能是相同的,在此上下文rerender意味着为全部组件调用render,这不是说react将unmount并remount全部组件,而是会根据上述的规则只应用到不同之处。

5.Context :避免使用props通过中间元素传递数据(比如爷爷传递数据给孙子)

用法:

const ThemContext = React.createContext(defaultValue); //defaultValue 只是在Provider不传value的时候起效。

class App extends React.Component {
    ...
    ...
    render() {
        return (
            //value就是被监听的数据.
            // 如果只是个单纯的对象,比如 value={{a: 'fff'}}
            // 会导致在Provider每次重新渲染时都传入新对象,Context会监听到数据改变导致重新渲染
            <ThemeContext.Provider value={this.state}>  
                <Father />
            </ThemeContext.Provider>
        );
    }
}

function Father(props) {
    return (
        <div>
            <Child />
        </div>
    )l
}

function Child(props) {
    return (
        <ThemeContext.Consumer>  //Consumer 会监听Provider的value变化
            {(theme) => ( <div>{theme.xx}</div> )}
        </ThemeContext.Consumer>
    );
}


//可以由多个Context 层层包裹
//Provider 和 Consumer可以是同个元素,只需要在value中传入改变的函数,类似props的回调
//在生命周期中访问context: 在Consumer中把theme作为props传下去
//转发Refs: 可以使用createRef()和 React.forwardRef((props, ref) => (
    <ThemeContext.Consumer>
        { theme => <FancyButton {...props} theme={theme} ref={ref} /> }
    </ThemeContext.Consumer>
));

 

6.Fragements

jsx需要有最顶层根标签包裹,在类似tr的标签中,只支持td,这是我们可以有<></>包裹td列表。<></> 语法不能接受键值或属性。

如果需要传入key,可以使用(只支持key)

<React.Fragment key={item.id}>
    <dt>{item.term}</dt>
    <dd>{item.description}</dd>
</React.Fragment>

7.Portals:将子节点渲染到父组件以外的 DOM 节点

import React from "react";
import ReactDOM from 'react-dom';

const appRoot = document.getElementById('root');
const modalRoot = document.getElementById('modal-root');

class Modal extends React.Component {

    constructor(props) {
        super(props);
        this.el = document.createElement('div');
    }
    componentDidMount() {
        modalRoot.appendChild(this.el);
    }
    componentWillUnMount() {
        modalRoot.removeChild(this.el);
    }

    render() {

        return ReactDOM.createPortal(
            this.props.children,
            this.el,
        );
    }
}

class Parent extends React.Component {
    constructor(props) {
        super(props);
        this.state = {clicks: 0};
        this.handleClick = this.handleClick.bind(this);
    }

    handleClick() {
        this.setState(prevState => ({
            clicks: prevState.clicks + 1
        }));
    }

    render() {
        return (
            <div onClick={this.handleClick}>
                <p>Number of Clicks: {this.state.clicks}</p>
                <Modal>
                    <Child />
                </Modal>    
            </div>
        );
    }
}

function Child() {
    return (
        <div className="modal">
            <button>Click</button>
        </div>
    )
}

export default Parent;

8. Error Boundaries 错误边界

概念: 用于捕捉其子组件树而不是整个组件树的异常,记录错误并展示一个回退的UI的React组件。

错误边界在渲染期间、生命周期方法内、以及整个组件树构造函数内捕获错误。

错误边界无法捕捉如下错误:

  • 事件处理:React不需要在错误边界中恢复位于事件处理器内的错误。不同于render方法或生命周期方法,事件处理器不是在渲染时发生。因此即使它们抛出异常,react仍然知道屏幕上要显示什么。如果需要在事件处理器内部捕获错误,使用try/catch
  • 异步代码(例如setTimeout或requestAnimationFrame回调函数)
  • 服务端渲染
  • 错误边界自身抛出来的错误(而不是其子组件)

定义: 一个类组件如果定义了生命周期方法 static getDerivedStateFromError(error) 或者 componentDidCatch(error, info)中的任意一个或两个,那它就是一个错误边界。

当一个错误被扔出后,使用 static getDerivedStateFromError()渲染一个退路UI, 使用componentDidCatch()去记录错误信息。

错误边界放到哪里: 粒度由你决定,你可以将其包装在最顶层的路由组件给用户“有东西出错”的提示,就像服务端框架经常处理崩溃一样。也可以将单独的插件包装在错误边界内以保护应用其他部门不崩溃。

从React 16起,任何未被错误边界捕获的错误将导致卸载整个React组件树。

try/catch非常棒,但其仅能用在命令式代码下,然而,React组件是声明式的并且要具体指出什么需要被渲染,错误边界保留了React的声明式的本质,其行为符合预期。

9.高阶组件(HOC) 一个接受一个组件为参数并return一个新组件的函数

例如redux的connect方法

高阶组件既不会修改输入组件,也不会使用继承拷贝他的行为,而是高阶组件组合原始组件,通过用一个容器组件包裹着原始组件。高阶组件就是一个没有副作用的纯函数。被包裹的组件接受容器的所有props属性或新data用于渲染。高阶组件并不关心数据使用的方式和原因,而被包裹的组件也不关心数据来自何处

  • 不要修改原始组件,而是使用组合,将原始组件包裹到一个容器组件中(容器组件是在于高层和底层间,进行职责分离策略的一部分。容器管理诸如订阅、状态和向管理渲染UI等事情的组件传递props,高阶组件使用容器作为他们实现的一部分,也可以认为高阶组件就是参数化的容器组件)
  • 贯穿传递不相关的props给被包裹的组件:
    render() {
        //筛选专用于这个组件不应该被贯穿传递的props属性
        const { extraProp, ...passThroughProps } = this.props;
    
        //向被包裹组件注入另外的props
        const injectedProp = someStateOrIntanceMethod;
    
        return (
            <WrappedComponent
                injectedProp={injectedProp}
                {...passThroughProps}
            />
        )
    }
  • 最大化的组合性: 高阶函数返回高阶组件
  • 包装显示名字以便于调试
    function withSubscription(WrappedComponent) {
        class WithSubscription extends React.Component { /* ... */ }
        WithSubscription.displayName = `WithSubscription(${getDisplayName(WrappedComponent)})`;
        return WithSubscription;
    }
    
    function getDisplayName(WrappedComponent) {
        return WrappedComponent.displayName || WrappedComponent.name || 'Component';
    }
  • 不要在render方法内使用高阶组件:每次调用高阶函数都会创建新的component,使得子对象树完全被卸载/重新加载,重新加载一个组件会引起原有组件的状态和他的所有子组件丢失。应该将高阶组件定义在外面,这样可以只创建一次组件。在很少的情况下用到动态的高阶组件时,可以在组件的生命周期方法或构造函数中操作。
  • 必须将静态方法做拷贝:当应用一个高阶组件时,尽管原始组件会被包裹在内,但新组件会没有原始组件的任何静态方法。所以在返回之前,将原始组件的方法拷贝给容器或者分别导出组件自身的静态方法。
    //必须得知道要拷贝的方法
    Enhance.staticMethod = WrappedComponent.staticMethod;
    
    //导出
    export { someFunction }
  • Refs属性不能贯穿:可用React.forward解决

10.render props : 使用一个值为函数的prop且能够让子组件知道要渲染什么内容的思路

例如:一个组件Mouse含有状态xy表示当前鼠标的坐标,另有一个组件Cat内部需要用到当前鼠标的坐标。此时可以使用这种思想

class Contain ...{
    ...
    <div>
        <Mouse getPos={mouse => (<Cat mouse={mouse} />)} />
    </div>
}

class Mouse ... {
    ...
    constructor(props){this.state = {x: 0, y: 0}}
    ...
    <div>
        {this.props.getPos(this.state)}
    </div>
}

class Cat ... {
    render() {
        const mouse = this.props.mouse;
        return (
            <p>{mouse.x}{mouse.y}</p>
        )
    }

}

⚠️在render中写箭头函数会生成新的函数,因此最好用构造函数bind(this)的形式

11.与第三方库协同:

注意如果有监听事件需要在didmount中注册且一定要在willunmount中销毁

12.可访问性

13.Code-Splitting

打包:构建就是将一个文件引入并合并到一个单独的文件中,这个单独的文件被称为包。

代码分割:当应用增长代码量增长后,需要分割代码以避免体积过大而使得加载时间过长。代码分割是由如webpack和browserify等打包器支持等一项能够创建多个包并在运行时动态加载的特性。

在你的应用中引入代码分割的最佳方式是通过动态import方法。

  • React Loadable: 将动态引入封装成了一个对React友好的API来在特定组件下引入代码分割的功能。
    import Loadable from 'react-loadable';
    
    const LoadableOtherComponent = Loadable({
        loader: () => import('./OtherComponent'),
        loading: () => <div>Loading...</div>
    });
    
    const MyComponent = () => {
        <LoadableOtherComponent />
    };

    它可以帮助创建加载状态、错误状态、超时、预加载等,甚至可以通过大量的代码分割帮忙进行服务端渲染。

  • 基于路由代码分割

14.严格模式

严格模式检查只在开发模式下运行,不会与生产模式冲突。

使用: 

render() {
    <div>
        <Header />
        <React.StrictMode>
            <div>
                <ComponentA />
                <ComponentB />
            </div>
        </React.StrictMode>
    </div>
}
//ComponentA、ComponentB及其后代将应用严格模式。Header不会。

StrictMode目前有助于:

  • 识别具有不安全生命周期的组件:某些老式生命周期将被打印出来。
  • 有关旧式字符串ref用法的警告:建议使用回调式和16.3的创建式: constructor中this.inputRef = React.createRef(); 组件中 ref = { this. inputRef }
  • 检测意外的副作用:
    • react在两个阶段起作用:
    1. 渲染阶段决定了需要对DOM进行哪些更改,在此阶段React调用render,然后将结果与上一次渲染进行比较(很慢)
      1. 包括的生命周期有:
        1. constructor
        2. componentWillMount
        3. componentWillReceiveProps
        4. componentWillUpdate
        5. getDerivedStateFromProps
        6. shouldComponentUpdate
        7. render
        8. setState的第一个形参
      2. 因为以上方法可能不止一次被调用,所以他们中不包含副作用尤其重要。忽略此规则可能会导致各种问题,包括内存泄漏和无效的程序应用状态。不幸的是很难发现这些问题,因为它们通常都是不确定的。严格模式不能自动检测副作用,但可以帮助发现使其更具有确定性。这是通过有意的双调用一下方法实现的)(只在开发模式有效,生产不会被双调用):
        1. constructor
        2. render
        3. setState的第一个形参
        4. static getDerivedStateFromProps
    2. 提交阶段是React执行更改的阶段。(在React DOM中,指React插入、更新和删除dom节点)。在此阶段React也调用生命周期,如componentDidMount和componentDidUpdate。

15.hooks :

  1. State Hook(在函数组件中使用state) : const [count, setCount] = useState(0);
    1. useState传入的是state的初始值,可以是数字字符串等不必非是对象。
    2. useState返回的是一个只有两个值的数组,第一个是该state的名字。第二个是更新该state的方法。
    3. 如果要有多个state,就写个多个useState。
    4. 不同于setState的合并,是覆盖
  2. Effect Hook(在函数组件中执行一些具有side effect(副作用)的操作):
    1. useEffect(() => {
          // 通过浏览器自带的 API 更新页面标题
          document.title = `You clicked ${count} times`;
        });
    2. 可以把 useEffect 视作 componentDidMountcomponentDidUpdate 和 componentWillUnmount 的结合
    3. 会在每次render后调用,即第一次调用和以后的每次update都运行。
    4. 在每次 render 的时候,我们传递给 useEffect 的方法都是全新的。这是故意的。事实上,这正是我们可以在 effect 内部读取到 count 值,并且不用担心 count 值过期的原因。每当我们重新 render 的时候,我们都会使用一个 不同的effect,替换掉之前的那一个。在某种程度上,这使得 effect 表现得更像是 render 结果的一部分————每个 effect “属于”一个特定的 render。
    5. 两类:
      1. 不需要清理的effects :比如上例、网络请求、手动更新 DOM 、以及打印日志都是常见的不需要清理的 effects。

      2. 需要清理的: 比如订阅(函数内注册,return返回注销的函数,匿名就可以)

        useEffect(() => {
            ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
            // 明确在这个 effect 之后如何清理它
            return function cleanup() {
              ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
            };
          });
        //上例会在每次render时调用即注销再注册
        
        //下例只会注册注销一次(mount,unmount), 但这样是不对的,props传来的id会变,但注册的订阅id还是旧的
        useEffect(() => {
            ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
            // 明确在这个 effect 之后如何清理它
            return function cleanup() {
              ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
            };
          }, []);
        
        //完美写法就是根据props的id是否改变决定是否重新注册订阅
        useEffect(() => {
            ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
            // 明确在这个 effect 之后如何清理它
            return function cleanup() {
              ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
            };
          },[props.friend.id]);
  3. dd

16.https://mp.weixin.qq.com/s/6MKPF3a8MXb_lRM6o4rggA

  1. 挂载期(出生)
    1. constructor
    2. static getDerivedStateFromProps(props, state) 基于props的变更来更新内部状态。在初始挂载到DOM之前调用。return { points: 200} 或者 null 表示更新state。以此方式获得的组件状态被称为派生状态。应该慎用派生状态
    3. render
    4. componentDidMount: 有时需要在组件挂载后立即从组件树中获取DOM节点,这时候就可以调用这个组件生命周期方法。网络请求。订阅。
  2. 更新期
    1. static getDerivedStateFromProps(props, state)
    2. shouldComponentUpdate: 默认情况下,大多数情况下,在state或props发生变更时会重新渲染组件,但可以通过return false控制不重新渲染
    3. render
    4. getSnapshotBeforeUpdate() 在DOM更新后从中获取一些信息。注意引用的是DOM更新之前的值。return的值将作为componentDidUpdate的第三个参数
    5. componentDidUpdate(prevProps, prevState, snapshot)  此时函数内取得的就是当前的DOM信息,因此可以利用snapshot计算两帧画面的区别。
  3. 卸载期
    1. componentWillUnmount 卸载和销毁前调用。这是进行资源清理最理想的地方。例如清除计时器、取消网络请求、清理订阅。
  4. 错误处理器
    1. static getDerivedStateFromError(error) { ... return {hasError: true}; } 只要后代组件抛出错误,错误就会被记录到控制台。return 一个对象,用于更新该组件的状态。不允许包含会产生副作用的代码。
    2. componentDidCatch(error, info) info包含更多信息。并且允许包含会产生副作用的代码。
      componentDidCatch(error, info) {
          logToExternalService(error, info) // this is allowed. 
              //Where logToExternalService may make an API call.
      }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值