什么是高阶组件
如果一个函数,接受一个或多个组件作为参数并且返回一个组件,就可以称之为高阶组件。
React中的高阶组件
React中的高阶组件主要有两种形式:属性代理(Props Proxy)和反向继承(Inheritance Inversion)。
1.属性代理
分两种情况:
情况一:无状态组件(Stateless Component)
function MapComponent(WrappedComponent) {
return <WrappedComponent />
}
情况二:有状态组件
const MapComponent = WrappedComponent => {
return class extends React.Component {
render() {
return (
<WrappedComponent {...this.props} />
)
}
}
}
属性代理是:一个函数接受一个 WrappedComponent 组件作为参数传入,并返回一个继承了 React.Component 组件的类,且在该类的 render() 方法中返回被传入的 WrappedComponent 组件。
React.Component能做什么,这个高阶组件就能做什么:
1).操作props
const MapComponent = WrappedComponent => {
return class extends React.Component {
render() {
// 将props的view变为null
const newProps = {
view: null
}
return (
<>
<WrappedComponent {...this.props} {...newProps} />
</>
)
}
}
}
2).操作state
//利用props和回调函数
const InputComponent = (WrappedComponent) => {
return class extends React.Component{
constructor(props) {
super(props);
this.state = {
name: '',
};
}
onChange = () => {
this.setState({
name: '这是一个文本框',
});
}
render() {
const newProps = {
name: {
value: this.state.name,
onChange: this.onChange,
},
};
return <WrappedComponent {...this.props} {...newProps} />;
}
};
}
//使用
const NameInput = props => (<input name="name" {...props.name} />);
export default InputComponent(NameInput);
3).状态判断:符合条件时才渲染传入的组件
// view初始化加载需要一定时间,因为作为一个判断条件,当view里有值时,才渲染组件
const MapComponent = WrappedComponent => {
return class extends React.Component {
render() {
const { view } = this.props;
return (
<>
{view ? <WrappedComponent {...this.props} /> : ''}
</>
)
}
}
}
2.反向继承
示例:
function MapComponent(WrappedComponent) {
return class extends WrappedComponent{
render() {
return super.render();
}
};
}
反向继承:一个函数接受一个 WrappedComponent 组件作为参数传入,并返回一个继承了该传入 WrappedComponent 组件的类,且在该类的 render() 方法中返回 super.render() 方法。
可以进行的操作:
1).操作state
高阶组件中可以读取、编辑和删除 WrappedComponent 组件实例中的 state。但是不建议这么做。
2).渲染劫持
操作render()输出的React元素树
const HigherOrderComponent = WrappedComponent => {
return class extends WrappedComponent {
render() {
const tree = super.render();
const newProps = {};
if (tree && tree.type === 'input') {
newProps.value = 'something here';
}
const props = {
...tree.props,
...newProps,
};
const newTree = React.cloneElement(tree, props, tree.props.children);
return newTree;
}
};
}
条件渲染
const UserComponent = WrappedComponent => {
return class extends React.Component {
render() {
const { user } = this.props;
if(user === 'admin') { //管理员
return <Admin />
} else { //其他用户
return <Common />
}
}
}
}
总结:属性代理和反向继承都是继承了某个父类的子类,属性代理中继承的是 React.Component,反向继承中继承的是传入的组件 WrappedComponent。
高阶组件的约定
- props 保持一致
- 你不能在函数式(无状态)组件上使用 ref 属性,因为它没有实例
- 不要以任何方式改变原始组件WrappedComponent
- 透传不相关 props 属性给被包裹的组件 WrappedComponent
- 不要再 render()方法中使用高阶组件
- 使用 compose 组合高阶组件
- 包装显示名字以便于调试
应用场景
1)系统的权限控制
const UserComponent = WrappedComponent => {
return class extends React.Component {
render() {
const { user } = this.props;
if(user === 'admin') { //管理员
return <Admin />
} else { //其他用户
return "您没有权限查看该页面!"
}
}
}
}
2).页面复用
把一个功能相同的部分封装为高阶组件,这样当多次使用时只需要调用高阶组件即可,从而实现页面的复用。
装饰者模式
定义:在不改变对象自身的前提下在程序运行期间动态的给对象添加一些额外的属性或行为。
因此,高阶组件就是装饰者模式在React中的实现:通过给函数传入一个组件(函数或类)后在函数内部对该组件(函数或类)进行功能的增强(不修改传入参数的前提下),最后返回这个组件(函数或类),即允许向一个现有的组件添加新的功能,同时又不去修改该组件。