1. 程式人生 > >React 16 新特性

React 16 新特性

2017年9月26日React 16釋出,通過官網和示例瞭解一下新特性。

React 16更新

新js環境要求

react16依靠Map和Set集合和requestAnimationFrame(一個針對動畫效果的API)

新功能

- Fragments:render函式可以返回陣列和字串
- error boundaries:錯誤處理
- portals :支援宣告性地將子樹渲染到另一個DOM節點
- custom DOM attributes :ReactDom允許傳遞非標準屬性
- improved server-side rendering:提升服務端渲染效能
  1. Fragments

    render() {
      return [
        <li key="A"/>First item</li>,
        <li key="B"/>Second item</li>,
        <li key="C"/>Third item</li>,
      ];
    }
    

    詳見API

  2. error boundaries

    之前,一旦某個元件發生錯誤,整個元件樹將會從根節點被unmount下來。React 16修復了這一點,引入了Error Boundary的概念,中文譯為“錯誤邊界”,當某個元件發生錯誤時,我們可以通過Error Boundary捕獲到錯誤並對錯誤做優雅處理,如使用Error Boundary提供的內容替代錯誤元件。Error Boundary可以看作是一種特殊的React元件,新增了componentDidCatch這個生命週期函式,它可以捕獲自身及子樹上的錯誤並對錯誤做優雅處理,包括上報錯誤日誌、展示出錯提示,而不是解除安裝整個元件樹。(注:它並不能捕獲runtime所有的錯誤,比如元件回撥事件裡的錯誤,可以把它想象成傳統的try-catch語句)

    實踐:

    抽象出檢查錯誤邊界公共元件:

    class ErrorBoundary extends React.Component{
        constructor(props){
            super(props);
            this.state=({
                ifError:false
            });
        }
    
        componentDidCatch(err, info) {
            this.setState({ ifError: true })
            console.log(err);
        }
    
        render(){
            if(this.state.ifError){
                return `this or its children has error`;
            }
            return this.props.children
        }
    }
    

    建立一個簡單的包含錯誤的子元件:

    class ErrorComponent extends React.Component{
        render(){
            const str = '123';
            return str.toFixed(2);
        }
    }
    

    使用錯誤邊界元件包裹可能出錯的元件

    class MainShowComponent extends React.Component{
        render(){
            return (
                <div>
                    <ErrorBoundary>
                        <ErrorComponent/>
                    </ErrorBoundary>
                </div>
            )
        }
    }
    

    當被錯誤邊界元件包裹的子元件中出現錯誤,會將錯誤元件替換為字串:this or its children has error,而不會導致整體元件樹被解除安裝。

  3. Portals

    Portals提供了一種一流的方法來將子代呈現到父元件的DOM層次結構之外的DOM節點。

    ReactDOM.createPortal(
      child,
      container
    );
    

    第一個引數(child)是任何可渲染的React子元素,如元素,字串或片段。第二個引數(container)是一個DOM元素。

    通常,當您從元件的render方法返回一個元素時,它將作為最近的父節點的子元素裝載到DOM中:

    render() {
      // React mounts a new div and renders the children into it
      return (
        <div>
          {this.props.children}
        </div>
      );
    }
    

    但是,有時將子項插入到DOM中的其他位置會很有用:

    render() {
      // React does *not* create a new div. It renders the children into `divNode`.
      // `divNode` is any valid DOM node, regardless of its location in the DOM.
      return React.createPortal(
        this.props.children,
        divNode,
      );
    }
    

    有關Portals 和其事件冒泡詳見官網CodePen例子

  4. custom DOM attributes

    支援非標準的自定義DOM屬性,在之前的版本中,React會忽略無法識別的HTML和SVG屬性,自定義屬性只能通過data-*形式新增,現在它會把這些屬性直接傳遞給DOM,這個改動讓React可以去掉屬性白名單,從而減少了檔案大小。但當DOM傳遞的自定義屬性是函式型別或event handler型別時,也會被React忽略掉。

    <div a={()=>{}}></div>   //錯誤
    
  5. improved server-side rendering

    提升服務端渲染效能,React 16的SSR被完全重寫,新的實現非常快,接近3倍效能於React 15,現在提供一種流模式streaming,可以更快地把渲染的位元組傳送到客戶端。

打破改變

  • 排程和生命週期的改變

    1. ReactDOM.render()和ReactDom.unstable_renderIntoContainer()如果在生命週期函式中呼叫將會返回null。所以解決此類問題可以使用portals或者refs
    2. setState的改變:

      • 呼叫setState返回null將不會更新render,這樣可以讓你在更新方法中自己決定是否更新。

        this.setState(
            (state)=>{
                if(state.curCount%2 === 0){
                    return {curCount:state.curCount+1}
                }else{
                    return null;
                }
        
            }
        )
        
      • 在render方法中呼叫setState總是會導致更新,之前版本不支援,但儘量不要在render中呼叫setState。

      • setState的回撥函式會在componentDidMount/ componentDidUpdate 執行之後立即執行,而不是在所有元件渲染之後。

            this.setState(
                (state)=>{
                    if(state.curCount%2 === 0){
                        return {curCount:state.curCount+1}
                    }else{
                        return null;
                    }
        
                },
                ()=>{
                    console.log(this.state.curCount);
                }
            )
        
    3. 當兩個元件<A /><B / >發生替換時,B.componentWillMount總是會在A.componentWillUnmount之前執行,而在之前,A.componentWillUnmount有可能會提前執行。

    4. 之前版本,當改變一個元件的ref時,ref和dom會在元件的render方法被呼叫之前分離。現在,我們延遲了ref的改變,直到dom元素被改變了,ref才會和dom分離。
    5. 對於不使用React而是使用其他方法來重新渲染容器是不安全的。這在以前的版本中也許會生效,但是我們覺得不支援這樣做。現在對於這種情況我們會發出一個警告,而且你需要使用ReactDOM.unmountComponentAtNode來清空你的節點樹。

      ReactDOM.render(<App />, div);
      div.innerHTML = 'nope';
      ReactDOM.render(<App />, div);//渲染一些沒有被正確清理的東西
      

      而你需要:

      ReactDOM.render(<App />, div);
      ReactDOM.unmountComponentAtNode(div);
      div.innerHTML = 'nope';
      ReactDOM.render(<App />, div); // Now it's okay
      
    6. componentDidUpdate生命週期不再接受prevContext引數。

    7. 使用不唯一的key可能會導致子元件的複製或者遺失,使用不唯一的key並不支援,並且也從未支援,但之前這是一個硬性錯誤。
    8. Shallow renderer(淺層渲染)不再觸發componentDidUpdate(),因為DOM的refs是不可用的。這也使得它與componentDidMount()之前版本中的呼叫一致。
    9. Shallow renderer不再支援unstable_batchedUpdates()。
    10. ReactDOM.unstable_batchedUpdates 現在回撥之後只有一個額外的引數。
  • 單檔案瀏覽器版本的名稱和路徑已經改變,以強調開發和生產版本之間的差異

    • react/dist/react.js → react/umd/react.development.js
    • react/dist/react.min.js → react/umd/react.production.min.js
    • react-dom/dist/react-dom.js → react-dom/umd/react-dom.development.js
    • react-dom/dist/react-dom.min.js → react-dom/umd/react-dom.production.min.js
  • 重寫並改進伺服器渲染器

    • 伺服器渲染不再使用標記驗證,而是盡力附加到現有的DOM,警告不一致。它也不再使用每個節點上的空白元件和資料反饋屬性的註釋。
    • 為伺服器渲染容器現在有一個明確的API。使用ReactDOM.hydrate而不是ReactDOM.render如果你正在恢復伺服器呈現的HTML。繼續使用,ReactDOM.render如果你只是做客戶端渲染。
  • 當未知的屬性傳遞給DOM元件時,如果是有效的值,React會渲染進DOM。檢視文件

  • 在render和生命週期函式中的錯誤預設會解除安裝整個DOM樹,為了阻止這個,可以在UI的相應位置新增錯誤邊界。

棄用

  • 不再構建react-with-addons.js,所有相容的外掛都是在npm上單獨釋出的,如果你需要它們,可以使用單檔案瀏覽器版本。
  • 在15.x版本中的棄用已經從核心包中刪除,React.createClass現在可以作為 create-react-class,React.PropTypes可以作為prop-types,React.DOM 作為 react-dom-factories,react-addons-test-utils 作為 react-dom/test-utils使用, shallow renderer 作為 react-test-renderer/shallow使用。參閱15.5.015.6.0文件參考。