HuangHongRui / huanghongrui.github.io

:poultry_leg: MyBlog | Keep track of every moment.. :icecream:
http://blog.luckyman.xyz/
3 stars 1 forks source link

React API #32

Open HuangHongRui opened 6 years ago

HuangHongRui commented 6 years ago

React V_16.3.0

1.即将淘汰的API.

componentWillMount
componentWillReceiveProps
componentWillUpdate

2.新增API

  1. componentDidUpdate() 该生命周期不经常需要用到的,但可用于在恢复期间手动保存滚动位置的情况。 与此同时这个新的生命周期功能涵盖componentWillUpdate。

  2. getDerivedStateFromProps(nextProps, prevState) state的更新基于props 该静态生命周期在组件实例化以及接收新 Props 后调用。 它可以 返回要更新的对象state,或是使用 null 指示新的 props, 让 state 不需要更新。

    class Example extends React.Component {
    static getDerivedStateFromProps(nextProps, prevState) {
    // ...
    }
    }
  3. getSnapshotBeforeUpdate(prevProps, prevState) 该新的 生命周期在进行突变之前被调用(例如,在DOM被更新之前)。 此生命周期的返回值将作为第三个参数传递给componentDidUpdate。

    class Example extends React.Component {
    getSnapshotBeforeUpdate(prevProps, prevState) {
    // ...
    }
    }

3. Example

  1. 初始化, 与之前不一, 弃用 componentWillMount , 直接初始化即可

    
    // Before
    class ExampleComponent extends React.Component {
    state = {};
    
    componentWillMount() {
    this.setState({
      currentColor: this.props.defaultColor,
      palette: 'rgb',
    });
    }
    }

// After class ExampleComponent extends React.Component { state = { currentColor: this.props.defaultColor, palette: 'rgb', } }


2. 异步请求数据
**componentWillMount 对于服务器呈现(其中不使用外部数据的地方)和即将到来的异步呈现模式(其中请求可能被多次启动)是有问题的。
常见的错误观念认为,componentWillMount可以避免第一个空的渲染状态。
React总是render在之后立即执行componentWillMount。如果数据在时间componentWillMount触发时不可用,则render无论您何时启动抓取,第一个仍将显示加载状态。这就是为什么componentDidMount在绝大多数情况下将提取移到没有明显效果的原因。**

// Before componentWillMount() { this._asyncRequest = asyncLoadData().then( externalData => { this._asyncRequest = null; this.setState({externalData}); } ); }

// After componentDidMount() { this._asyncRequest = asyncLoadData().then( externalData => { this._asyncRequest = null; this.setState({externalData}); } ); }


3.state的更新基于props
// 生命周期在组件创建时以及每次收到新道具时调用

// Before // componentWillReceiveProps生命周期往往是误用在这方面做存在的问题。因此,该方法将被弃用。 componentWillReceiveProps(nextProps) { if (this.props.currentRow !== nextProps.currentRow) { this.setState({ isScrollingDown: nextProps.currentRow > this.props.currentRow, }); } } // After static getDerivedStateFromProps(nextProps, prevState) { if (nextProps.currentRow !== prevState.lastRow) { return { isScrollingDown: nextProps.currentRow > prevState.lastRow, lastRow: nextProps.currentRow, }; }


4. 调用外部回调
**有时候会使用错误使用componentWillUpdate,在componentDidUpdate发生错误时,更新其他组件的状态已经“太晚了”。React可确保在用户看到更新的用户界面之前setState发生的任何调用componentDidMount和componentDidUpdate刷新。通常,最好避免像这样的级联更新,但在某些情况下它们是必需的(例如,如果在测量渲染的DOM元素之后需要定位工具提示)。

无论哪种方式,componentWillUpdate在异步模式下使用此功能都是不安全的,因为对于单个更新,外部回调可能会被多次调用。相反,componentDidUpdate应该使用生命周期,因为它保证每次更新只能调用一次**

// Before class ExampleComponent extends React.Component { componentWillUpdate(nextProps, nextState) { if ( this.state.someStatefulValue !== nextState.someStatefulValue ) { nextProps.onChange(nextState.someStatefulValue); } } } // After class ExampleComponent extends React.Component { componentDidUpdate(prevProps, prevState) { if ( this.state.someStatefulValue !== prevState.someStatefulValue ) { this.props.onChange(this.state.someStatefulValue); } } }


5. Props导致的副作用
**像componentWillUpdate,componentWillReceiveProps可能会多次调用一次更新。
出于这个原因,避免在此方法中引入副作用非常重要。相反,componentDidUpdate应该使用,因为它[ 保证 ]每次更新只能调用一次:
**

// Before class ExampleComponent extends React.Component { componentWillReceiveProps(nextProps) { if (this.props.isVisible !== nextProps.isVisible) { logVisibleChange(nextProps.isVisible); } } } // After class ExampleComponent extends React.Component { componentDidUpdate(prevProps, prevState) { if (this.props.isVisible !== prevProps.isVisible) { logVisibleChange(this.props.isVisible); } } }


6. props更改时获取外部数据

// Before componentWillReceiveProps(nextProps) { if (nextProps.id !== this.props.id) { this.setState({externalData: null}); this._loadAsyncData(nextProps.id); } } // After static getDerivedStateFromProps(nextProps, prevState) { if (nextProps.id !== prevState.prevId) { return { externalData: null, prevId: nextProps.id, }; } return null; } componentDidUpdate(prevProps, prevState) { if (this.state.externalData === null) { this._loadAsyncData(this.props.id); } }


7. 在更新之前, 读取 DOM 
**componentWillUpdate用于读取DOM属性。然而,对于异步渲染,“渲染”阶段生命周期(如componentWillUpdate和render)与“提交”阶段生命周期(如componentDidUpdate)之间可能会出现延迟。如果用户在这段时间内做了类似调整窗口大小的操作,则scrollHeight读取的值componentWillUpdate将会过时。
解决这个问题的方法是使用新的“提交”阶段生命周期getSnapshotBeforeUpdate。在进行改变之前立即调用此方法(例如,在更新DOM之前)。它可以返回一个值作为参数传递给React,在其改变后立即componentDidUpdate调用它。**

// Before class ScrollingList extends React.Component { listRef = null; previousScrollHeight = null;

componentWillUpdate(nextProps, nextState) { // Are we adding new items to the list? // Capture the current height of the list so we can adjust scroll later. if (this.props.list.length < nextProps.list.length) { this.previousScrollHeight = this.listRef.scrollHeight; } }

componentDidUpdate(prevProps, prevState) { // If previousScrollHeight is set, we've just added new items. // Adjust scroll so these new items don't push the old ones out of view. if (this.previousScrollHeight !== null) { this.listRef.scrollTop += this.listRef.scrollHeight - this.previousScrollHeight; this.previousScrollHeight = null; } }

render() { return (

{/* ...contents... */}
);

}

setListRef = ref => { this.listRef = ref; }; }

// After class ScrollingList extends React.Component { listRef = null;

getSnapshotBeforeUpdate(prevProps, prevState) { // Are we adding new items to the list? // Capture the current height of the list so we can adjust scroll later. if (prevProps.list.length < this.props.list.length) { return this.listRef.scrollHeight; } return null; }

componentDidUpdate(prevProps, prevState, snapshot) { // If we have a snapshot value, we've just added new items. // Adjust scroll so these new items don't push the old ones out of view. // (snapshot here is the value returned from getSnapshotBeforeUpdate) if (snapshot !== null) { this.listRef.scrollTop += this.listRef.scrollHeight - snapshot; } }

render() { return (

{/* ...contents... */}
);

}

setListRef = ref => { this.listRef = ref; }; }