高级指引!

一、代码分割

二、context使用

类似于vue的provider和inject属性,react提供了React.createContext方法,创建一个上下文组件,被该组件所包裹的组件可以无需通过props传递属性,直接深传递进组件树。

const ThemeContext = React.createContext(context)
class App extends React.componet {
    render () {
        return (
            <ThemeContext.provider>
                <Children />
            </ThemeContext.provider>
        )
    }
}
class Children extends React.Component {
    render () {
        return (
            <div theme={this.context}></div>
        )
    }
}

context很方便,但是会使得组件的复用性变差,因此context一般用于管理应用的locale,theme等状态。
而对于业务组件,为了防止props层层传递,其实还有一种方法。

三、控制反转

当你的组件嵌套较深,而且有某个state仅在较底层的组件使用时,我们可以将组件作为props传递,从而将底层组件提升至较为高层的组件中声明。然后在子组件中使用this.props[组件名]来将其渲染

function Page(props) {
  const user = props.user;
  const userLink = (
    <Link href={user.permalink}>
      <Avatar user={user} size={props.avatarSize} />
    </Link>
  );
  return <PageLayout userLink={userLink} />;
}

// 现在,我们有这样的组件:
<Page user={user} avatarSize={avatarSize} />
// ... 渲染出 ...
<PageLayout userLink={...} />
// ... 渲染出 ...
<NavigationBar userLink={...} />
// ... 渲染出 ...
{props.userLink}

四、错误边界

如果一个class组件定义了static getDerivedStateFromError或者componentDidCatch这两个生命周期方法中的任意一个时,那么它就变成了一个错误边界,当子组件抛出错误时,会被最近的错误边界所捕获。

class ErrorBoundary extends React.Component {
    constructor (props) {
        super(props)
        this.state = { hasError: false }
    }
    static getDerivedStateFromError () {
        // 更新 state 使下一次渲染能够显示降级后的UI
        return { hasError: true }
    }

    render () {
        if (this.state.hasError) {
            return <ErrorComponent/>
        }

        return this.props.children
    }
}

五、ref

通过React.createRef方法可以创建一个ref对象,然后在指定的组件上声明ref属性为该ref对象之后,我们便可以通过该ref上的current属性,获取当前组件的真实dom元素

class Child extends React.Component {
  constructor () {
    super();
    this.childRef = React.createRef()
  }

  componentDidMount () {
    window.a = this.childRef
    console.log(this.childRef.current)
  }

  render () {
    return (
      <section ref={this.childRef}>
        i am child
      </section>
    )
  }
}

另外,当创建的ref作用于组件时,那么this.ref.current将会返回组件的实例。

六、HOC高阶组件

HOC组件接收一个组件和多余参数,进行AOP面向切面编程,返回一个新的组件,HOC是纯函数,没有副作用,当我们的输入相同时其一定返回一样的结果,它不会修改传入的组件,也不会继承,而是通过包装提供通用逻辑,得到一个被包装后的原始组件。

function logProps (Component) {
    return class extends React.Component {
        comonentDidUpdate (prevProps) {
            console.log(`prevProps: ${prevProps}`);
            console.log(`props: ${this.props}`);
        }
        return <Component { ...this.props } />;
    }
}

七、react diff算法

以后详细看看,粗浅地看貌似和vue的diff差不多,只是少了头尾节点对比的过程,也有通过key进行原地复用的优化,还有同层级比较的优化。

八、render props

当我们有一个通用的高阶组件,被其包裹的组件将包含某些能力。当被包裹的组件无需读取高阶组件包含的状态时,我们可以在高阶组件内部直接读取this.props.children来渲染插入的自定义组件,但是当自定义组件需要用到高阶组件的状态,上述方法就不适用了,我们可以通过给高阶组件一个render props,再将高阶组件的state传入自定义组件中。

class HOCComponent extends React.Component {
    constructor (props) {
        super(props)
    }

    componentDidMounted () {
        console.log('hello world !')
    }

    render () {
        return (
            <>
                {this.props.render(this.state)}
            </>
        )
    }
}

class Child extends React.Component {}
class Wrap extends React.Component {
    render () {
        return (
            <HOCComponent render={
                state => (
                    <Child hocState={state}>
                )
            }/>
        )
    }
}

九、使用propTypes对props进行类型检查

类似于vue的prop属性,props是组件的静态属性,用于校验传入的props类型,另外,通过defaultProps静态属性还可以对传入的props赋予默认值,一个普通的propTypes如下:

import PropTypes from 'prop-types';
class Greeting extends React.Component {
    static propTypes = {
        name: PropTypes.string.isRequire,
        age: PropTypes.oneOf([
            PropTypes.string,
            PropTypes.number
        ])
    }
    static defaultProps = {
        name: 'zw'
    }
    render () { return (<div>{ this.props.name }</div>) }
}