React 16 新特性使用總結

WinMin發表於2018-09-28

從React 16.0開始,React引入了一下新的特性(元件、API、生命週期函式)。從15.X升級到16.X是一種增強式的升級即之前的用法在新版上保持有效,基本是平滑的升級,但是隨著React 16.5的釋出,新特性基本已經完善和固定下來,為了享受新版本帶來的更高效率架構和更規範的開發流程,現在是時候使用上React ^16.0 的新特性進行程式碼重構。

我直接使用了React 16.5,具體請檢視React 更新日誌以下是幾個常用的新特性,每個特性都從與之前的比較和例項來說明:

一、生命週期函式

生命週期函式的改動有兩個方面

新增:

static getDerivedStateFromProps(nextProps, prevState) getSnapshotBeforeUpdate(prevProps, prevState) componentDidCatch(error, info)

不安全:

UNSAFE_componentWillMount(nextProps, nextState) UNSAFE_componentWillReceiveProps(nextProps) UNSAFE_componentWillUpdate(nextProps, nextState)

在16.X版本中依然可以使用不安全的周期函式(UNSAFE_字首可加可不加),用法也和之前的一樣。但是如果使用了新增的周期函式就不能同時使用不安全的周期函式,否則會報錯並且不安全的周期函式不會被執行

使用新增的周期函式的執行順序還是按照階段來說明:

裝載階段

constructor -> getDerivedStateFromProps -> render -> componentDidMount

更新階段

getDerivedStateFromProps -> shouldComponentUpdate -> render -> getSnapshotBeforeUpdate -> componentDidUpdate

解除安裝階段

componenWillUnMount

新增的生命週期函式

getDerivedStateFromProps周期函式的作用是根據props初始化或更新state,有兩個引數分別是nextProps(本次渲染的props)和prevState(當前元件的state),判斷nextProps和prevState對應的屬性是否相同,返回需要更改的state。

React 15.X 初始化和更新分別寫在constructor和componentWillReceiveProps.
class Parent extends Component{
    render(){
        return (
        <Child age={18} />
        )
    }
}
class Child extends Components{
    constructor(props){
        super(props);
        this.state = {
            age: this.props.age //裝載階段初始化state
        }
    }
    componentWillReceiveProps(nextProps){
        if(nextProps.age !== this.state.age){
            this.setState({
                age: nextProps.age
            }) // 修改state
        }
    }
    render(){...}
}

React 16.5 寫在getDerivedStateFromProps即可
class Child extends Component{
    constructor(props){
        super(props);
        this.state = {
            age: ''
        }
    }
    static getDerivedStateFromProps(nextProps,prevState){
        if(nextProps.age !== prevState.age){
            return {
                age: nextProps.age
            }
        }
        return null;
    }
    render(){...}
}
複製程式碼

getSnapshotBeforeUpdate作用是替代componentWillUpdate,有兩個引數分別是prevProps(未更新前的props)和prevState(未更新前的state)。最大的區別是它放在了render函式後執行,為將來的Fiber架構做準備;還有的區別是componetWillUpdate的引數是nextProps(本次更新的props)和nextState(本次更新的state),在轉換時需要注意。

列表項數目改變時scroll到最後一條
 getSnapshotBeforeUpdate(prevProps, prevState) {
    if (prevProps.list.length !== this.props.list.length) {
      const list = this.listRef.current //dom渲染前的列表元素ref
      return list.scrollHeight - list.scrollTop
    }
    return null
  }
    
  componentDidUpdate(prevProps, prevState, snapshot) {
    // snapshot 是getSnapshotBeforeUpdate返回的偏移量
    if (snapshot !== null) {
      const list = this.listRef.current //dom渲染後的列表元素ref
      list.scrollTop = list.scrollHeight - snapshot
    }
  }

複製程式碼

componentDidCatch React元件發生錯誤時的處理邏輯,呼叫第三方的錯誤監聽服務。

二、Context

使用Context 主要是解決props向多層巢狀的子元件傳遞的問題,原理是定義了一個全域性物件。新的Context的使用方法跟舊的有很大差別

// React 15.x需要自己開發定義提供 Context的父元件,然後用這個父元件包裹巢狀多層的子元件。
class Provider extends Component {
    getChildContext(){
        return {
            age: 18
        }
    }
    render() {
        return this.props.children;
    }
}
Provider.childContextTypes = {
    age: PropTypes.number
};
// 子元件
class Child extends Component {
    constructor(props,context) { 
        super(props,context); //super 帶上context 引數
    }
    render(){
        const {age} = this.context; //使用this.context訪問
        ...
    }
}
Child.contextTypes = {
    age: PropTypes.number
};

// React 16.5 使用 React.createContext()
const MyContext = React.createContext();
// 使用 MyContext.Provider 包裹子元件
<MyContext.Provider value={{age: 18}}>
    <Child />
</MyContext.Provider>
//子元件使用 MyContext.Consumer獲得 Context
class Child extends Component {
    render(){
       return (
       <MyContext.Consumer>
            {(context) => {
                const {age} = context;
                return ...
            }}
       </MyContext.Consumer>
       )
    }
}

複製程式碼

三、Portal 和 Refs

Portal 是React 16 的全新特性,可以將元件渲染到非根節點其他 DOM 節點上,因為之前版本沒有這個功能,所以使用場景需要逐步發掘。用法是使用ReactDOM.createPortal(component, domNode) component是要渲染的元件,domNode是掛載的dom節點。

React 16 使用React.createRef 建立Ref物件,以下是使用的區別:

// React 15.X
class Parent extends Component {
    getChildRef(ref){
        this.myRef = ref;
    }
    render() {
        <Child childRef={this.getChildRef} />
    }
}
class Child extends Component {
  constructor(props) {
    super(props)
  }

  render() {
    return <div ref={this.props.childRef} />
  }
}

// React 16.5
class Parent extends Component {
  constructor(props) {
    super(props)
    this.myRef = React.createRef()
  }
  render() {
      return <Child ref={this.myRef} />
  }
}

const Child = React.forwardRef((props, ref) =>  <div ref={ref} />);

複製程式碼

相關文章