参考博客(高阶组件):https://blog.youkuaiyun.com/sinat_17775997/article/details/100761756
高阶函数:如果一个函数 接受一个或多个函数作为参数或者返回一个函数 就可称之为 高阶函数
function withGreeting(greeting = () => {}) {
return greeting;
}
高阶组件:如果一个函数 接受一个或多个组件作为参数并且返回一个组件 就可称之为 高阶组件
当高阶组件中返回的组件是 无状态组件(Stateless Component) 时,该高阶组件其实就是一个高阶函数,因为无状态组件 本身就是一个纯函数。
function HigherOrderComponent1(WrappedComponent) {
return <WrappedComponent / >
}
有状态高阶组件:
function HigherOrderComponent2(WrappedComponent) {
return class extends React.Component{
render() {
return <WrappedComponent {...this.props} />;
}
}
}
实现高阶组件的方法:属性代理(Props Proxy)和 反向继承(Inheritance Inversion);
属性代理(Props Proxy)类型的高阶组件中可以做什么,比如:
- 操作 props
- 抽离 state
- 通过 ref 访问到组件实例
- 用其他元素包裹传入的组件 WrappedComponent
①操作 props
function HigherOrderComponent(WrappedComponent) {
return class extends Component {
constructor(props){
supre(props);
this.state = {};
}
render() {
const newProps = {
name: '大板栗',
age: 18,
};
// console.log(this.props);
return <WrappedComponent {...this.props} {...newProps} />;
}
};
}
export default HigherOrderComponent;
②抽离 state
function HigherOrderComponent(WrappedComponent) {
return class extends Component {
constructor(props){
super(props);
this.state = {
name: ""
};
}
render() {
const newProps = {
name:{
value: this.state.name,
onChange: this.onChange,
}
};
// console.log(this.props);
return <WrappedComponent {...this.props} {...newProps} />;
}
onChange = (name) => {
this.setState({
name: name,
});
}
};
}
export default HigherOrderComponent;
③通过 ref 访问到组件实例
function HigherOrderComponent(WrappedComponent) {
return class extends React.Component {
render() {
return <WrappedComponent {...this.props} ref={this.Method} />;
}
// ref 的值可以是字符串(不推荐使用)也可以是一个回调函数,如果是回调函数的话,它的执行时机是:
// 组件被挂载后(componentDidMount),回调函数立即执行,回调函数的参数为该组件的实例。
// 组件被卸载(componentDidUnmount)或者原有的 ref 属性本身发生变化的时候,此时回调函数也会立即执行,且回调函数的参数为 null
Method = (instance) => {
// wrappedComponentInstance.someMethod();
console.log("我被调用了:");
console.log(instance);
// instance.test();
console.log(instance.box.current);
}
};
}
export default HigherOrderComponent;
④用其他元素包裹传入的组件 WrappedComponent
function HigherOrderComponent(WrappedComponent){
return class extends React.Component {
render(){
return (
<div>
<h1>欢迎来到高阶组件</h1>
<WrappedComponent />
</div>
)
}
}
}
export default HigherOrderComponent;
模仿redux中的connect:
function connect(cb){
return (WrappedComponent)=>{
return class extends React.Component{
render(){
let obj = cb();
// console.log(obj);
return <WrappedComponent {...this.props} {...obj} />
}
}
}
}
export default connect;
调用connect:
export default connect(()=>{
return{
name:"张三",
age: 18
}
})(App);
反向继承(Inheritance Inversion)
最简单的反向继承实现:
function HigherOrderComponent(WrappedComponent) {
return class extends WrappedComponent {
render() {
return super.render();
}
};
}
反向继承其实就是 一个函数接受一个 WrappedComponent 组件作为参数传入,并返回一个继承了该传入 WrappedComponent 组件的类,且在该类的 render() 方法中返回 super.render() 方法。
会发现其属性代理和反向继承的实现有些类似的地方,都是返回一个继承了某个父类的子类,只不过属性代理中继承的是 React.Component,反向继承中继承的是传入的组件 WrappedComponent。
反向继承可以用来做什么:
- 操作 state
- 渲染劫持(Render Highjacking)
①操作 state
高阶组件中可以读取、编辑和删除 WrappedComponent 组件实例中的 state。甚至可以增加更多的 state 项,但是 非常不建议这么做因为这可能会导致 state 难以维护及管理。
②渲染劫持(Render Highjacking)
之所以称之为 渲染劫持 是因为高阶组件控制着 WrappedComponent 组件的渲染输出,通过渲染劫持我们可以:
- 有条件地展示元素树(element tree)
- 操作由 render() 输出的 React 元素树
- 在任何由 render() 输出的 React 元素中操作 props
- 用其他元素包裹传入的组件 WrappedComponent (同 属性代理)
高阶组件的约定
高阶组件带给我们极大方便的同时,我们也要遵循一些约定:
- props 保持一致
- 你不能在函数式(无状态)组件上使用 ref 属性,因为它没有实例
- 不要以任何方式改变原始组件 WrappedComponent
- 透传不相关 props 属性给被包裹的组件 WrappedComponent
- 不要再 render() 方法中使用高阶组件
- 使用 compose 组合高阶组件
- 包装显示名字以便于调试