HOC / Render Props & Hooks 都是代码复用的方式, 但是三者的区别和应用场景又分别是什么?

Render Props

Render Props, 在 React 组件之间使用一个值为函数的 prop 共享代码的技术(父子之间的信息传递); render prop 是一个用于告知组件需要渲染什么内容的函数 prop; 具有 render prop 的组件接受一个函数, 该函数返回一个 react 元素并调用它而不是实现自己的渲染逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 先将 withRenderProps 封装为一个 Component
// 同时这个 Component 留了一个 props 用来接收需要渲染的组件
const withRenderProps = ({ render }) => {
const [state, setState] = useState('initValue')
useEffect(() => {
// ...
setState('value')
})
return render(state)
}
const Example = ({ s }) => {
return <div>{ s }</div>
}
const App = () => {
return <withRenderProps render={ () => <Example s={s}/> } />
}

HOC

HOC(Higher-Order-Component) 高阶组件, 是 React 中用于复用组件逻辑的一种技巧, 自身不是 React API 的一部分, 它是一种基于 React 的组合特性而形成的设计模式; 是一个参数为组件, 返回值为新组件的函数, 这么做是为了增强 (enhance), 如装饰、添加行为、添加逻辑等

1
2
3
4
5
6
7
8
9
// hoc 的定义及使用
let hoc = (`组件私有参数`) => {
return class extends React.Component {
// ...
}
}
hoc(value1, value2...)
// react redux 中的 connect 函数
let reduxHoc = connect(mapStateToProps, mapDispatchToProps)(Component)
  • 使用 HOC 解决横切关注点(Cross-Cutting Concerns)问题: 当代码逻辑大部分实现都是一样的, 且调用的地方比较多, 需要抽象出来, 在一个地方定义这个逻辑, 并在许多组件之间共享它

    注意: HOC 不会修改传入的组件, 也不会使用继承来复制其行为, 相反, HOC 通过将组件包装在容器组件中来组成新组件, HOC 是纯函数, 没有副作用

  • 不要改变原始组件, 使用组合: HOC 不应该修改传入组件, 而是应该使用组合的方式, 通过将组件包装在容器组件中实现功能
  • 约定: 将不相关的 props 传递给被包裹的组件
  • 约定: 最大化可组合性
  • 约定: 包装显示名称方便调试

注意:

  1. 不要在 render 方法中使用 HOC
  2. 务必复制静态方法
  3. Refs 不会被传递
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 封装成一个函数 withHOC, 这个函数返回一个新的 Component
const withHOC = (WrappedComponent) => {
return () => {
const [state, setState] = useState('initValue')
useEffect(() => {
// ...
setState('value')
})
return <WrappedComponent s={state} />
}
}
const Example = ({ s }) => {
return <div>{ s }</div>
}
const ExampleWithHOC = withHOC(Example);
const App = () => { return <ExampleWithHOC /> }

Hooks

Hooks

1
2
3
4
5
6
7
8
9
10
11
12
13
// 封装成 useHooks, 在其他地方可以调用 useHooks
const useHooks = () => {
const [state, setState] = useState('initValue')
useEffect(() => {
// ...
setState('value')
})
return state
}
const Example = () => {
const s = useHooks()
return <div>{s}</div>
}

总结

  • Render Props: 可以在树状 VDOM 的渲染流程上有更多的自由度, 可以根据父组件提供的数据进行动态渲染; 适合有明确父子关系的场景
  • HOC: 适合用来做注入, 并且生成一个新的可复用组件, 也适合用来做插件
  • Hooks: 可以优化 render 的前置逻辑处理(提取复用逻辑), 也适合用于取代 class extend 的大部分用例

就像 async/await 将 Promise 的 Callback Hell 解决了, 将异步编程思维变成了同步变成思维; HOC & Render Props 容易产生 Wrapper Hell 问题, 而 Hooks 则解决了这一问题, 将”面向生命周期编程“变成了”面向业务逻辑编程“