让 render-xxx 模式均可以互换。javascript
全部上面提到的三种模式都是为了处理 mixin 要处理的问题的。在咱们继续以前,咱们来看一些例子。java
若是你已经掌握这两个概念,能够直接跳过这一节看后面的内容react
首先,咱们来写一个组件来记录 count,并绘制 render prop 里的绘制都在 render 方法里调用了。git
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 }); } }
注意:callback 属性也能够叫个别的名字,不必定就是 render。只不过这个模式叫作 render prop。github
接下来咱们须要一个组件来把须要的内容绘制到屏幕上:this
const Counter = ({ count, update }) => { <div> <button onClick={() => update("Inc")}>Inc</button> {count} <button onClick={() => update("Dec")}>Dev</button> </div>; };
Counter
组件接受 count 值和一个 update 方法,而后显示一个增长、一个减小按钮以及当前的 count 值。code
最后咱们能够用CounterComponent
组件来给Counter
增长一些其余的功能:component
render( <CounterComponent render={props => <Counter {...props} />>} />, document.getElementById('root') );
上文讲解了 render prop 模式。如今来看看怎么使用高阶组件来达到一样的目的。ip
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} {} /> } };
看上面的例子,咱们能够要把须要绘制的组件放到另一个全新的组件里面去。在这个新的组件里包含了增强的 state 和 props 等内容。get
const CounterExample = withCounter(Counter); render(<CounterExample />, document.getElementById("root"));
目前咱们已经覆盖了基本的概念。咱们可使用不一样的模式来达到相同的目的。下面咱们来关注一下如何让这几个模式达到互换的目的。
有的时候你用的库提供了一个高级组件,可是你最喜欢的是经过JSX的方式来使用组件。有时会遇到一个提供了 render props 的库,可是你喜欢的是高阶组件。一个颇有趣的事实是这些模式能够幻想转换。
咱们来根据上面的例子来加一些方法可让高阶组件和 render props 模式能够互相转换。
fromHoc: HOC -> RenderProp toHoc: RenderProp -> HOC
toHoc
方法能够归结为:
toHoc: Render => Comp => props => ( <Render {...Props} render={props => <Comp {...props} />} /> );
你也能够看使用 Render Props来做为替代实现。
它会把一个 render prop 模式转化为高阶组件。
const withCounter = toHoc(CounterComponent); const CounterExample = withCounter(Counter);
从高阶组件转化为 render prop 有一点麻烦。咱们须要把一个 render prop 的组件传入到高阶组件里。多亏了 Brent Jackon 的这篇文章。
fromHoc: hoc => { class Render extends React.Component { render() { return this.props.children(this.props); } } return hoc(Render); };
或者,使用两外一种不用 class 的方式。此次要感谢 Rodrigo Pombo 的这个例子。
fromHoc: hoc => hoc(props => props.render(props));
咱们能够写一个轻量的 helper 方法来实现高阶组件和 renderprops 的转化。注意,咱们也能够在初始化 toHoc 方法的时候使用另外的 render name,由于 render prop 能够能有一个不同的名字,或者是一个子 prop。
const iso = { fromHoc: hoc => hoc(props => props.render(props)), toHoc: Render => Compo => props => ( <Render {...props} render={props => <Comp {...props} />} /> ) };
Render prop,回调绘制和高阶组件都是能够互换的。大多数的时候 render props 能够知足要求。可是你也能够经过某些方法让它在这些模式之间互相转换。
很是感谢 Brent Jackson,Kent C. Dodds 以及 Rodrigo Pombot 提供了fromHoc
方法的实现,帮咱们解决了很多的问题。