React组件抽象(一):高阶组件
前面介绍了组件抽象的方法:mixin。现在我们来介绍下另一种重要的高阶组件(Higher-order Component),简写HOC。
一、高阶组件是什么,为什么要使用?
组件被包裹,返回一个增强的组件。
打个比方,HOC像钢铁侠的机甲,对于普通人来说,穿上就是复联超人了,这个人并不一定需要关心内部怎么机甲实现方式。
为啥要用?被包裹组件不需要关心高阶组件发生了什么,逻辑基本上相互独立,没有啥感知。
二、如何实现高阶组件
高阶组件实现方式,大体有两种:
- 属性代理(常用)
- 反向继承
这两种方法基本都能做到类似或者同样的事情,比如渲染劫持、控制props;
渲染劫持:高阶组件根据内部自己的状态控制被包裹组件的显示状态,比如未加载配置需要Loading等;
控制props:你都被人包住了,props不都得随便人操作:
2.1 属性代理(推荐)
通过包裹组件,给组件传递处理好的props。
- 简单的例子
import React from "react";
const HOCWrapper = WrappedComponent => {
const userInfo = {name:"张全蛋"}
return class extends React.Component{
render() {
return <WrappedComponent userInfo={userInfo} {...this.props}/>;
}
}
}
class List extends React.Component{
render(){
return <div>{this.props.userInfo.name}</div>
}
}
export default HOCWrapper(List)
如上述例子HOCWrapper内部可以做一些操作,获得用户信息。子组件被高阶组件包裹、属性经过代理,就多了userInfo的props属性。被包裹的组件可以直接使用props里面的userInfo信息。
使用修饰器之后可以写成这样:
@HOCWrapper
export default class Main extends React.Component{
render(){
return <div>{this.props.userInfo.name}</div>
}
}
2.2 反向继承
根据文字知道,高阶组件继承了被包裹组件,返回了一个新的组件。
- 渲染劫持
import React from "react";
const HOCWrapper = WrappedComponent => {
return class extends WrappedComponent{
state = {loading:true}
componentDidMount(){
fetchData().then(data => {
this.setState({loading: false})
})
}
render() {
if(this.state.loading){
return <Loading/>
}
return (
<div>
{super.render()}
</div>
)
}
}
}
class Main extends React.Component{
render(){
return <div>MAIN</div>
}
}
- 控制props
import React from "react";
const HOCWrapper = WrappedComponent => {
return class extends WrappedComponent{
render() {
const node = super.render();
const props = Object.assign({},node.props,{userInfo:this.state.userInfo})
const newNode = React.cloneElement(node,props,node.props.children);
return (
<div>
{newNode}
</div>
)
}
}
}
- 控制state
控制高阶组件的state就相当于控制被包裹组件的state。
import React from "react";
const HOCWrapper = WrappedComponent => {
return class extends WrappedComponent{
changeName = () =>{
this.setState({name:"唐马儒"})
}
render() {
return (
<div onClick={this.changeName}>
{super.render()}
</div>
)
}
}
}
class Main extends React.Component{
state = {name:"张全蛋"}
render(){
return <div>MAIN</div>
}
}
虽然控制被包裹组件state很方便,但是这样会给组件带来隐藏的state,无法像props一样能够预测渲染结果。
mixin给组件注入新功能的时候,也可能导致加入一些隐藏的状态,维护组件的人难免会踩坑;
所以建议要尽量少用这种方式。
三、其他
3.1 带参数的高阶组件
相当于调用方法,方法返回值是一个高阶组件的wrapper。
需求是这样的:每个组件都要控制权限,权限在用户信息里面。组件的权限Code是确定的。
import React from "react";
const HOCWrapper = permissionCode => WrappedComponent => {
const userInfo = {name:"张全蛋",permissionList:["100.200.300"]};
return class extends React.Component{
render() {
if(userInfo.permissionList.includes(permissionCode)){
return <div>没有权限! Permit denied!</div>
}
return <WrappedComponent userInfo={userInfo} {...this.props}/>;
}
}
}
@HOCWrapper("100.200.300")
export default class Main extends React.Component{
render(){
return <div>{this.props.userInfo.name}</div>
}
}
这里执行了HOCWrapper("100.200.300")是不是返回了一个高阶组件工具呢?
3.2 带来的问题
习惯用React-dev-tool的同学们肯定知道在使用Redux的时候会有一堆的connent();层次嵌套,一个页面最高有几十个组件嵌套,看起来非常头疼。(没错,redux就是高阶组件的使用方式)
这时候需要这样处理:
- 给组件命名
import React from "react";
const HOCWrapper = WrappedComponent => {
const userInfo = {name:"张全蛋"}
const HOC = class extends React.Component{
render() {
return <WrappedComponent userInfo={userInfo} {...this.props}/>;
}
}
HOC.displayName = `HOC-${WrappedComponent.displayName}`
return HOC;
}
- React 16.8 Hook
用hook 替换redux,有兴趣的可以了解下
3.3 贴近项目的使用场景
单页应用在每次刷新页面的时候去服务器获取用户信息,获取数据期间需要显示Loading。返回成功后才显示页面。
import React from "react";
const HOCWrapper = permissionCode => WrappedComponent => {
return class extends React.Component{
state = {
loading:true,
userInfo:{
name:"张全蛋",
permissionList:[]
}
};
// 组件加载完成获取用户数据
componentDidMount() {
fetchData("getUserInfo").then(data =>{
this.setState({
loading:false,
userInfo:data
})
})
}
render() {
// 刚开始未获取到数据的时候显示Loading
if(this.state.loading){
return <Loading/>
}
const { userInfo } = this.props;
// 获取到数据后校验权限
if(userInfo.permissionList.includes(permissionCode)){
return <div>没有权限! Permit denied!</div>
}
// 正常渲染数据
return <WrappedComponent userInfo={userInfo} {...this.props}/>;
}
}
}
@HOCWrapper("100.200.300")
export default class Main extends React.Component{
render(){
return <div>{this.props.userInfo.name}</div>
}
}
3.4 建议或者总结
属性代理的方式是最常见的,简单好理解。
反向继承我觉得有秀操作的嫌疑,但是我没有证据。
高阶组件+修饰器是绝配,在你的项目里用起来吧。
本文深入探讨React中高阶组件(HOC)的概念,包括其作用、实现方式及应用场景,对比属性代理与反向继承两种方法,并讨论带参数HOC及其实现技巧。
410

被折叠的 条评论
为什么被折叠?



