React 16

15 5月

React16的一些特性:

  • Fragment
  • Portals
  • lazy,Suspense

Fragment

根节点用div会导致DOM里多出不少额外的div,可以用React.Fragment代替。当然最主要的原因是:有时候组件需要返回多个元素,根节点不能是div。例如表格td组件:

class Columns extends React.Component {
    render() {
        // 根节点如果是div,将无法使用,因为tr里里没有div
        return (
            <React.Fragment>
                <td>Hello</td>
                <td>World</td>
            </React.Fragment>
        );
    }
}

React.Fragment支持空标签的简写形式,当然你要确认一下IDE支不支持这样的写法。

<>
    <td>Hello</td>
    <td>World</td>
</>

Portals

正常情况下,组件的render方法返回的元素会被装载到最近父节点DOM中,如果想将它装载到其他DOM节点中,可以用插槽。常被用于Modal,hover等,能让元素在父组件有 overflow: hidden或z-index样式的情况下,产生一种跳出容器的感觉。

在React15里,可以用unstable_renderSubtreeIntoContainer不稳定的API,实现插槽:

ReactDom.unstable_renderSubtreeIntoContainer(
    parentComponent
    component,
    domContainer,
    callback,
);    

React16里,可以用ReactDOM.createPortal(child, container)来创建插槽,参数child是React子组件,参数container是任何有效的DOM节点。

尽管插槽可以让React子节点安置在DOM树的任何地方,但其他方面其行为和普通的React子节点行为一致,仍存在于React tree中,不用考虑其在DOM tree中的位置。

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

const modalRoot = document.getElementById('root');

class Modal extends 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,
        );
    }
}

export default class Parent extends Component {
    constructor(props) {
        super(props);
        this.state = { clicks: 0 };
    }

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

    render() {
        return (
            <div onClick={this.handleClick}>
                <p>点击次数: {this.state.clicks}</p>
                <p>打开调试面板,button不在该div内</p>
                <Modal>
                    <div className="modal">
                        <button>点击</button>
                    </div>
                </Modal>
            </div>
        );
    }
}

lazy,Suspense

React.lazy可以支持动态加载组件,要和React.Suspense配合使用,否则会报错,而且还不可用于服务器端渲染。如果要在服务器渲染中进行代码拆分,建议使用Loadable

import React, { Component, lazy, Suspense } from 'react';
import ErrorBoundary from './ErrorBoundary';

const OtherComponent = lazy(() => import('./OtherComponent'));

export default class MyComponent extends Component {
    render() {
        return (
            <div>
                <ErrorBoundary>
                    <Suspense fallback={ <div>Loading...</div> }>
                        <OtherComponent />
                    </Suspense>
                </ErrorBoundary>
            </div>
        );
    }
}

React.lazy接受一个函数,必须调用动态import(),返回Promise,resolves一个带有包含default导出模块的React组件。

React.Suspense组件的fallback属性接受在等待时渲染的React组件。React.Suspense可以放在要懒加载组件上方的任何位置。

发表评论

电子邮件地址不会被公开。 必填项已用*标注