React 进阶

Context

Context 是 React 中比较特殊的对象,是一种组件中传值的特殊方式。

因为 React 中的数据流向是单向的,所以如果需要在层级很深的组件树中传值的话,需要不断地从 props 中向下传值,Context 则解决了这个问题(不同层级的组件需要访问同样一些数据)。

React 相关 API:

API作用
React.createContext创建一个 Context 对象,接收一个“默认值”作为参数
Context.Provider一个 React 组件,接受一个 value 的属性值。被这个组件包裹的子组件都可以使用这个属性值。
contextType将一个组件的 static 属性 contextType 设置为对应的 Context 对象,即可在这个组件内部的任意生命周期函数中,通过 this.context 访问到 Provider 提供的属性值。
Context.Cosumer一个 React 组件,接受一个函数作为其包裹的内容,函数的参数即为 Provider 的 value 值。

注意事项:

  1. context 的默认值,只有在 Cosumer 的外层没有 Provider 包裹的时候使用;
  2. Provider 支持多层嵌套;
  3. 消费组件的更新只取决于 Provider 是否更新,不会执行 shouldComponentUpdate

高阶组件

定义:高阶组件(high order components)是参数为组件且返回值也为组件的函数。

编写方式

根据高阶组件内容的编写方式可以分为“组合”和“反向继承”两种方式。

组合:因为这种方式的编写中通常是对属性的一些操作,所以又被称为“属性代理”:

1
2
3
4
5
6
7
const hoc = data => Wrapper => {
  return class HOC extends React.Component {
    render() {
      return <Wrapper {...this.props} {...ExtraPorps} />
    }
  }
}

PS: 这里体现了函数式编程的“柯里 curry 化”思想,即将接受多个参数的函数拆分成接受单个参数的多个函数。

反向继承:返回的新组件继承于新组件:

1
2
3
4
5
6
7
const LOGHOC = Wrapper => {
  return class HOC extends Wrapper {
    render() {
      return super.render();
    }
  }
}

React 高阶组件的编写比较推荐使用“组合”的方式,以后继承会对原来组件的一些内容进行修改。

使用场景

场景一:操作 props

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
export const PropsHOC = Wrapper => logContent => {
  return class HOC extends React.Component {
    handleClick = () => {
      console.log(logContent);
      this.props.onClick();
    }
    render() {
      return <Wrapper onClick={this.handleClick} />
    }
  }
}

场景二:通过 ref 访问组件实例;

场景三:组件状态提升;

场景四:用其他元素包装组件。

注意事项与调试技巧

  1. 不要改变组件原型:

    • 即不要通过 prototype 等方式影响组件本身的特性。建议通过组合的方式新增函数。
  2. 应该将不相关的 props,透传给被包裹的组件。

  3. 通过将函数柯里化最大化高阶组件的可组合性。

  4. 给组件设置 displayName 属性,可以在 chrome 进行调试时展示对应组件的名称。

  5. 不要在任何生命周期方法中使用 HOC。

Fragment/Portal

Fragment 的作用?

  • Fragment 支持在代码中聚合子元素的列表,并不会在 DOM 中添加新的节点(通常用 div 实现)。

Portal 的作用?

  • Portal 支持实现像弹窗类似的功能;
  • 它在 DOM 上会将对应的元素传送到视图树的指定位置,但是响应冒泡仍然遵循 React 代码中的定义。

调和思想

O(n) 事件复杂度的 diff 算法。为了实现列表的重复检测,给定了 key 属性。