React 高阶组件、Render props、hooks 有什么区别,为什么要不断迭代
这三者是目前react解决代码复用的主要方式:
-
高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。具体而言,高阶组件是参数为组件,返回值为新组件的函数。
-
render props是指一种在 React 组件之间使用一个值为函数的 prop 共享代码的简单技术,更具体的说,render prop 是一个用于告知组件需要渲染什么内容的函数 prop。
-
通常,render props 和高阶组件只渲染一个子节点。让 Hook 来服务这个使用场景更加简单。这两种模式仍有用武之地,(例如,一个虚拟滚动条组件或许会有一个 renderltem 属性,或是一个可见的容器组件或许会有它自己的 DOM 结构)。但在大部分场景下,Hook 足够了,并且能够帮助减少嵌套。
HOC
官方解释∶
高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。
高阶组件(HOC)就是一个函数,且该函数接受一个组件作为参数,并返回一个新的组件,它只是一种组件的设计模式,这种设计模式是由react自身的组合性质必然产生的。我们将它们称为纯组件,因为它们可以接受任何动态提供的子组件,但它们不会修改或复制其输入组件中的任何行为。
简言之,HOC是一种组件的设计模式,HOC接受一个组件和额外的参数(如果需要),返回一个新的组件。HOC 是纯函数,没有副作用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| function withSubscription(WrappedComponent, selectData) { return class extends React.Component { constructor(props) { super(props); this.state = { data: selectData(DataSource, props) }; } render() { return <WrappedComponent data={this.state.data} {...this.props} />; } };
const BlogPostWithSubscription = withSubscription(BlogPost, (DataSource, props) => DataSource.getBlogPost(props.id));
|
1)HOC的优缺点
2)适用场景
-
代码复用,逻辑抽象
-
渲染劫持
-
State 抽象和更改
-
Props 更改
3)具体应用例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| function withAdminAuth(WrappedComponent) { return class extends React.Component { state = { isAdmin: false, } async UNSAFE_componentWillMount() { const currentRole = await getCurrentUserRole(); this.setState({ isAdmin: currentRole === 'Admin', }); } render() { if (this.state.isAdmin) { return <WrappedComponent {...this.props} />; } else { return (<div>您没有权限查看该页面,请联系管理员!</div>); } } }; }
class PageA extends React.Component { constructor(props) { super(props); } UNSAFE_componentWillMount() { } render() { } } export default withAdminAuth(PageA);
class PageB extends React.Component { constructor(props) { super(props); } UNSAFE_componentWillMount() { } render() { } } export default withAdminAuth(PageB);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| class Home extends React.Component { render() { return (<h1>Hello World.</h1>); } } function withTiming(WrappedComponent) { return class extends WrappedComponent { constructor(props) { super(props); this.start = 0; this.end = 0; } UNSAFE_componentWillMount() { super.componentWillMount && super.componentWillMount(); this.start = Date.now(); } componentDidMount() { super.componentDidMount && super.componentDidMount(); this.end = Date.now(); console.log(`${WrappedComponent.name} 组件渲染时间为 ${this.end - this.start} ms`); } render() { return super.render(); } }; }
export default withTiming(Home);
|
注意:withTiming 是利用 反向继承 实现的一个高阶组件,功能是计算被包裹组件(这里是 Home 组件)的渲染时间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| const withFetching = fetching => WrappedComponent => { return class extends React.Component { state = { data: [], } async UNSAFE_componentWillMount() { const data = await fetching(); this.setState({ data, }); } render() { return <WrappedComponent data={this.state.data} {...this.props} />; } } }
export default withFetching(fetching('science-fiction'))(MovieList);
export default withFetching(fetching('action'))(MovieList);
export default withFetching(fetching('some-other-type'))(MovieList);
|
Render props
官方解释∶
"render prop"是指一种在 React 组件之间使用一个值为函数的 prop 共享代码的简单技术
具有render prop 的组件接受一个返回React元素的函数,将render的渲染逻辑注入到组件内部。在这里,"render"的命名可以是任何其他有效的标识符。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class DataProvider extends React.Components { state = { name: 'Tom' }
render() { return ( <div> <p>共享数据组件自己内部的渲染逻辑</p> { this.props.render(this.state) } </div> ); } }
<DataProvider render={data => ( <h1>Hello {data.name}</h1> )}/>
|
由此可以看到,render props的优缺点也很明显∶
Hooks
官方解释∶
Hook是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。通过自定义hook,可以复用代码逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13
| function useSubscription() { const data = DataSource.getComments(); return [data]; }
function CommentList(props) { const {data} = props; const [subData] = useSubscription(); ... }
<CommentList data='hello' />
|
以上可以看出,hook解决了hoc的prop覆盖的问题,同时使用的方式解决了render props的嵌套地狱的问题。hook的优点如下∶
需要注意的是:hook只能在组件顶层使用,不可在分支语句中使用。
总结
Hoc、render props和hook都是为了解决代码复用的问题,但是hoc和render props都有特定的使用场景和明显的缺点。hook是react16.8更新的新的API,让组件逻辑复用更简洁明了,同时也解决了hoc和render props的一些缺点。