探索React(第七期):高阶组件与Render Props

103 阅读3分钟

我保持年轻的秘诀:谎报年龄~

组件设计模式 - 高阶组件

01.png const NewComponent = highOrderComponent(OldComponent)
具体而言,高阶组件就是以组件作参数,并返回一个新的组件。而高阶组件可以很轻松的吧外部协议化注入到一个基础Component中,所以比较适合用来做插件。在实际开发中我们可以使用高阶组件来封装具有权限控制的逻辑高阶组件。

const withAdminAuth = (WrappedComponent) => {
  class HOC extends React.Component {
    state = {
      permission: false,
      content: "",
    };
    handleClick = (e) => {
      this.setState({
        permission: false,
        content: e.target.innerText,
      });
    };
    handlePermissonClick = () => {
      this.setState({
        permission: true,
        content: "",
      });
    };
    render() {
      if (this.state.permission) {
        return (
          <div onClick={this.handleClick}>
            <WrappedComponent {...this.props} />
          </div>
        );
      } else {
        return (
          <div onClick={this.handlePermissonClick}>您还没有权限呢,亲~</div>
        );
      }
    }
  }
  return HOC;
};
export default withAdminAuth;

const ItemA = (props) => {
  return (
    <div>
      <button>ItemA</button>
      <div>{props.content}</div>
    </div>
  );
};
export default withAdminAuth(ItemA);

function App() {
  return (
    <div>
      <ItemA content={"test"} />
    </div>
  );
}

在上述代码中,我们使用了点击事件来替代了业务中的权限处理的逻辑,在开发中,我们可以在withAdminAuth组件中添加获取并判断当前用户身份是否具有权限的处理逻辑。而我们的ItemA组件则是将自己作为参数传递给withAdminAuth从而复用了权限点击变化的相关逻辑。
使用了高阶组件对代码进行复用之后,可以非常轻便的进行扩展,哪天产品经理没事干了,说PageB,C,D,E...页面都需要权限才能展示,我们只需要在相关页面嵌套一层withAdminAuth就好啦~

Render Props

Render Props也是一种在不重复代码的情况下共享组件间功能的方法,通过props来定义呈现的内容,组件只是注入功能,而不需要知道它如果应用于UI。

class RenderProps extends React.Component {
  state = {
    permission: false,
    content: "",
  };
  handleClick = (e) => {
    this.setState({
      permission: false,
      content: "",
    });
  };
  handlePermissonClick = () => {
    this.setState({
      permission: true,
      content: "test",
    });
  };
  render() {
    if (this.state.permission) {
      return (
        <div onClick={this.handleClick}>{this.props.render(this.state)}</div>
      );
    } else {
      return <div onClick={this.handlePermissonClick}>您还没有权限呢,亲~</div>;
    }
  }
}
export default RenderProps;

const ItemA = (props) => {
  return (
    <div style={{ width: "200px", height: "200px" }}>
      <WithAdminAuth
        render={({ permission, content }) => (
          <div>
            <button>ItemA</button>
            {permission && <span>{content}</span>}
          </div>
        )}
      />
    </div>
  );
};
export default ItemA;

function App() {
  return (
    <div>
      <ItemA />
    </div>
  );
}

我们将上面的代码稍微改造一下,可以看出高阶组件是将传入的组件作为参数,转换为另一个组件,而Render Props则是将class组件的state作为props传递给纯函数组件。当然this.props.render也可以使用this.props.children替代,这样看起来会更加直观,改造后代码如下~

class RenderProps extends React.Component {
  state = {
    permission: false,
    content: "",
  };
  handleClick = (e) => {
    this.setState({
      permission: false,
      content: "",
    });
  };
  handlePermissonClick = () => {
    this.setState({
      permission: true,
      content: "test",
    });
  };
  render() {
    if (this.state.permission) {
      return (
        <div onClick={this.handleClick}>{this.props.children(this.state)}</div>
      );
    } else {
      return <div onClick={this.handlePermissonClick}>您还没有权限呢,亲~</div>;
    }
  }
}
export default RenderProps;

const ItemA = (props) => {
  return (
    <div style={{ width: "200px", height: "200px" }}>
      <WithAdminAuth>
        {({ permission, content }) => (
          <div>
            <button>ItemA</button>
            {permission && <span>{content}</span>}
          </div>
        )}
      </WithAdminAuth>
    </div>
  );
};
export default ItemA;

function App() {
  return (
    <div>
      <ItemA />
    </div>
  );
}

总结

总的来说,Render Props其实与高阶组件类似,对于HOC模式来说,复用性比较强,可以多层嵌套,但是也产生了一些问题,即可能会加深组件的层级,当多个HOC一起使用的时候,很难判断出子组件二点props是由哪个HOC负责传递的,而Render Props则不会产生无用的组件加深层级。
Render Props模式比HOC更直观也更利于调试,而HOC可传入多个参数,能减少不少的代码量,所以对于这两者设计模式的选择,应该根据不同的应用场景抉择。