mixin

Mixin 设计模式

Mixin(混入)是一种通过扩展收集功能的方式,它本质上是将一个对象的属性拷贝到另一个对象上面去,可以拷贝多个属性到一个对象上,为了解决代码复用问题。

常用的方法:JQuery 的 extend 方法。

var LogMixin = {
  log: function() {
    console.log('log');
  },
  componentDidMount: function() {
    console.log('in');
  },
  componentWillUnmount: function() {
    console.log('out');
  }
};

var User = React.createClass({
  mixins: [LogMixin],
  render: function() {
    return (<div>...</div>)
  }
});

var Goods = React.createClass({
  mixins: [LogMixin],
  render: function() {
    return (<div>...</div>)
  }
});

缺点

  1. Mixin 可能会相互依赖,相互耦合,不利于代码维护
  2. 不同的 Mixin 中的方法可能会相互冲突
  3. 现在大量使用 ES6 语法后,React.createClass 已经取消,这种方式也不再推荐

高阶组件(HOC)

高阶组件的定义:

高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。 (高阶组件是参数为组件,返回值为新组件的函数。)

具体的意思就是:
高阶组件可以看作 React 对装饰模式的一种实现,高阶组件就是一个函数,且该函数接受一个组件作为参数,并返回一个新的组件。他会返回一个增强的 React 组件。高阶组件可以让我们的代码更具有复用性,逻辑性与抽象性,可以对 render 方法进行劫持,也可以控制 props 与 state。

const EnhancedComponent = higherOrderComponent(WrappedComponent);

实现高阶组件的两种方式:

  1. 属性代理: 高阶组件通过被包裹的 React 组件来操作 props
  2. 反向继承:高阶组件继承于被包裹的 React 组件。

属性代理

import React,{Component} from 'react';

const HOC = (WrappedComponent) =>
  class WrapperComponent extends Component {
    state = { number: 0 };
    btnClick = () => {
        this.setState({
            number: this.state.number++
        })
    }
    render() {
        const newProps = {
            btnClick: this.btnClick,
            number: this.state.number
        }
        return (
            <div>
                rere HOC
                <WrappedComponent {...this.props} {...this.newProps} />
            </div>
        )
    }
}

export default HOC;

class MyComponent extends Component{
    //...
}

export default HOC(MyComponent)

这里最重要的部分是render 方法中返回的 WrappedComponent 的 React 组件,这样就可以通过高阶组件来传递 props,这就是属性代理。

这样组件就可以一层层地作为参数被调用,原始组件就具备了高阶组件对它的修饰,也保持了单个组件的封装性,与易用性。

特点

  1. 控制 props
    我们可以在 HOC 里面对 props 进行增删查改等操作

    const MouseHoc = (MouseComponent, props) => {
       props.text = props.text + "---I can operate props";
       return class extends React.Component {
           render() {
               return (
                   <div style={{ height: "100%" }} onMouseMove={this.handleMouseMove}>
                       <MouseComponent {...props} mouse={this.state} />
                   </div>
               );
           }
       };
    };
    MouseHoc(Mouse, {
       text: "some thing..."
    });
  2. 通过 refs 使用引用

    function refHOC(WrappedComponent) {
        return class extends Component {
            componentDidMount() {
                this.wapperRef.log();
            }
            render() {
                return (
                    <WrappedComponent
                        {...this.props}
                        ref={ref => {
                            this.wapperRef = ref;
                        }}
                    />
                );
            }
        };
    }
  3. 抽象 state
  4. 渲染劫持
    高阶组件可以在 render 函数中做非常多的操作,从而控制原组件的渲染输出。只要改变了原组件的渲染,我们都将它称之为一种渲染劫持。参考 前端react面试题详细解答

    function visibleHOC(WrappedComponent) {
       return class extends Component {
           render() {
               if (this.props.visible === false) return null;
               return <WrappedComponent {...props} />;
           }
       };
    }
  5. 使用其他元素包裹 WrappedCompoennt
    修改 props

反向继承

const MyContainer = (WrappedComponent) =>
    class extends WrappedComponent{
        render(){
            return super.render();
        }
    }

这里返回的组件继承了 WrappedComponent,因为被动地继承了 WrappedComponent,所有的调用都会反向。

注意事项

  1. 当我们应用 HOC 去增强另一个组件时,我们实际使用的组件已经不是原组件了,所以我们拿不到原组件的任何静态属性,我们可以在 HOC 的结尾手动拷贝他们

    function proxyHOC(WrappedComponent) {
       class HOCComponent extends Component {
           render() {
           return <WrappedComponent {...this.props} />;
           }
       }
       HOCComponent.staticMethod = WrappedComponent.staticMethod;
       // ...
       return HOCComponent;
    }
2. 不要在 render 方法内创建高阶组件

3. 不要改变原始组件(高阶组件就是一个没有副作用的纯函数。)

4. 透传不相关的 props

### 解决的问题

1. 高阶组件就是一个没有副作用的纯函数,各个高阶组件不会互相依赖耦合
2. 高阶组件也有可能造成冲突,但我们可以在遵守约定的情况下避免这些行为
3. 高阶组件并不关心数据使用的方式和原因,而被包裹的组件也不关心数据来自何处。高阶组件的增加不会为原组件增加负担

### 存在的问题

1. HOC 需要在原组件上进行包裹或者嵌套,如果大量使用 HOC,将会产生非常多的嵌套,这让调试变得非常困难
2. HOC 可以劫持 props,存在相同名称的 props,则存在覆盖问题在不遵守约定的情况下也可能造成冲突,而且 react 并不会报错。
3. 当存在多个 HOC 时,你不知道 Props 是从哪里来的。
4. HOC 属于静态构建,静态构建即是重新生成一个组件,即返回的新组件,不会马上渲染,即新组件中定义的生命周期函数只有新组件被渲染时才会执行。

**在这个范式下,代码通过一个类似于 装饰器(decorator) 的技术进行共享。首先,你的一个组件定义了大量需要被渲染的标记,之后用若干具有你想用共享的行为的组件包裹它。因此,你现在是在 装饰 你的组件,而不是混入你需要的行为!

## Render props

> Render Props 从名知义,也是一种剥离重复使用的逻辑代码,提升组件复用性的解决方案。在被复用的组件中,**通过一个名为“render”(属性名也可以不是 render,只要值是一个函数即可)的属性,该属性是一个函数,这个函数接受一个对象并返回一个子组件,会将这个函数参数中的对象作为 props 传入给新生成的组件**。

### 它的特点

1. 传入函数的属性,就是  想要共享的 state,这个相同的 state 是组件的状态,或者行为
2. 术语 “render prop” 是指一种技术,用于使用一个值为函数的 prop 在 React 组件之间的代码共享。
3. render prop 仅仅就是一个函数。
4. `render prop`是一种模式,重点是 prop,而不是 render,任何被用于告知组件需要渲染什么内容的函数 prop 在技术上都可以被称为 “render prop”.
5. 这里的组合模型是 动态的!每次组合都发生在 render 内部,因此,我们就能利用到 React 生命周期以及自然流动的 props 和 state 带来的优势。
6. 具有 render prop 的组件接受一个函数,该函数返回一个 React 元素并调用它,而不是实现自己的渲染逻辑

### 小栗子

<DataProvider render={data => (
<h1>Hello {data.target}</h1>
)}/>


#### 优势

1. 不用担心 Props 是从哪里来的, 它只能从父组件传递过来。
2. 不用担心 props 的命名问题。
3. render props 是动态构建的。

可以用 `render props`来代替 HOC

const withMouse = (Component) => {
return class extends React.Component {

render() {
  return <Mouse render={mouse => (
    <Component {...this.props} mouse={mouse}/>
  )}/>
}

}
}


## Hook

在 Hooks 出现以前,我们总是纠结的问题:

1. 无状态组件 VS Class 组件

2. 生命周期
   componentDidMount 和 componentDidUpdate 需要做相同的事情

3. this 指向
   为了保证 this 的指向正确,我们经常这么写
   

this.handleClick = this.handleClick.bind(this)
<button onClick={() => this.handleClick(e)}>


4. HOC 和 render props 增加我们代码的层级关系

动机

Hook 的作用

1. 我们的函数变成了一个有状态的函数
2. Hooks 本质上就是一类特殊的函数,它们可以为你的函数型组件(function component)注入一些特殊的功能(生命周期钩子的功能:useEffect;上下文(context):useContext)
3. 解决 this 指向问题

### State Hooks

### Effect Hooks

我们写的有状态组件,通常会产生很多的副作用(side effect)。之前都把这些副作用的函数写在生命周期函数钩子里,比如 componentDidMount,componentDidUpdate 和 componentWillUnmount。而现在的 useEffect 就相当与这些声明周期函数钩子的集合体。它以一抵三。

用 Effect Hooks 来解决这个这些副作用。

#### 注意点

1. react 首次渲染和之后的每次渲染都会调用一遍传给 useEffect 的函数。而之前我们要用两个声明周期函数来分别表示首次渲染(componentDidMount),和之后的更新导致的重新渲染(componentDidUpdate)。
2. 函数是异步执行的,而之前的 componentDidMount 或 componentDidUpdate 中的代码则是同步执行的
3. 怎么解绑副作用
4. 跳过一些不必要的副作用函数

### 使用范围

只能在 React **函数式组件或自定义** Hook 中使用 Hook。

Hook 的提出主要就是为了解决 class 组件的一系列问题,所以我们不能在 class 组件中使用它。

相比函数,编写一个 class 可能需要掌握更多的知识,需要注意的点也越多,比如 this 指向、绑定事件等等。另外,**计算机理解一个函数比理解一个 class 更快。Hooks 让你可以在 classes 之外使用更多 React 的新特性。**

紧张的大葱
35 声望3 粉丝