Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HOC和renderProps互相转化 #19

Open
xiaoxiaosaohuo opened this issue Jun 16, 2018 · 0 comments
Open

HOC和renderProps互相转化 #19

xiaoxiaosaohuo opened this issue Jun 16, 2018 · 0 comments

Comments

@xiaoxiaosaohuo
Copy link
Owner

现有一个counter组件

用renderProps实现

class CounterComponent extends React.Component {
  constructor(props) {
   super(props);
   this.state = { count: 0 };
  }
  update = type => {
    if (type === "Inc") {
      this.setState(({ count }) => ({ count: count + 1 }));
    } else if (type === "Dec") {
      this.setState(({ count }) => ({ count: count - 1 }));
    }
  };
  render() {
    return this.props.render({
      ...this.state,
      ...this.props,
      update: this.update
    });
  }
}

渲染函数中,我们合并Props,状态并添加更新函数并将其传递给renderProps。

我们还需要一个组件渲染元素到屏幕上

const Counter = ({ count, update }) => (
  <div>
    <button onClick={() => update("Inc")}>Inc</button>
      {count}
    <button onClick={() => update("Dec")}>Dec</button>
  </div>
);

render(
  <CounterComponent render={props => <Counter {...props} />} />,
  document.getElementById("root")
);

这是renderProp的写法

再来看看HOC

import React from "react";
import { render } from "react-dom";

const withCounter = Component =>
  class Hoc extends React.Component {
    constructor(props) {
      super(props);
      this.state = { count: 0 };
    }
    update = type => {
      if (type === "Inc") {
        this.setState(({ count }) => ({ count: count + 1 }));
      } else if (type === "Dec") {
        this.setState(({ count }) => ({ count: count - 1 }));
      }
    };
    render() {
      return <Component {...this.state} {...this.props} update={this.update} />;
    }
  };

const Counter = ({ count, update }) => (
  <div>
    <button onClick={() => update("Inc")}>Inc</button>
    {count}
    <button onClick={() => update("Dec")}>Dec</button>
  </div>
);
const CounterExample = withCounter(Counter);

render(<CounterExample />, document.getElementById("root"));

以上两种方式能实现同样的功能,有时候你想用HOC实现功能,但是在还是喜欢JSX的组装方式,有时候你想用renderProps,但是给组件增强多个功能,其实呢,这两种方法可以互相转换的。

我们写两个转换函数

fromHoc: HOC -> RenderProp
toHoc: RenderProp -> HOC

toHoc 转换renderProps to HOC

toHoc: Render => Comp => props => ( 
  <Render {...props} render={props => <Comp {...props} />} />
)
const withCounter = toHoc(CounterComponent);
const CounterExample = withCounter(Counter);

fromHoc 这种方式就略微复杂了一些,我们需要将具有renderPrps的组件传递给实际的高阶组件

fromHoc: hoc => {
  class Render extends React.Component {
    render() { 
      return this.props.children(this.props);
    }
  }
  return hoc(Render);
}

或者

fromHoc: hoc => hoc(props => props.render(props));

例子

import React from "react";
import { render } from "react-dom";

const withCounter = Component =>
  class Hoc extends React.Component {
    constructor(props) {
      super(props);
      this.state = { count: 0 };
    }
    update = type => {
      if (type === "Inc") {
        this.setState(({ count }) => ({ count: count + 1 }));
      } else if (type === "Dec") {
        this.setState(({ count }) => ({ count: count - 1 }));
      }
    };
    render() {
      return <Component {...this.state} {...this.props} update={this.update} />;
    }
  };

const Counter = ({ count, update }) => (
  <div>
    <button onClick={() => update("Inc")}>Inc</button>
    {count}
    <button onClick={() => update("Dec")}>Dec</button>
  </div>
);

const fromHoc = hoc => hoc(props => props.render(props));

const CounterComponent = fromHoc(withCounter);

render(
  <CounterComponent render={props => <Counter {...props} />} />,
  document.getElementById("root")
);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant