⓵ 高阶组件的概念及应用
⓶ 以数组为子组件的模式
高阶组件
一个高阶组件就是一个函数,这个函数接受一个组件作为输入, 然后返回一个新的组件作为结果, 而且,返回的新组件拥有了数组组件所不具有的功能
定义高阶组件的意义:
- 重用代码。 把公用逻辑提取出来,利用高阶组件的方式应用出去
- 修改现有 React 组件的行为。
告诫组件的实现方式:
- 代理方式的高阶组件
- 继承方式的高阶组件
代理方式的高阶组件
应用场景:
1. 操纵prop;
2. 访问ref;
3. 抽取状态;
4. 包装组件。
1. 操纵prop
/**
* @Author zula
* @DateTime 2017-10-13
* @param {[type]} WrappedComponet [原始组件]
* @param {[type]} newProps [需添加的props]
* @return {[type]} [高阶组件]
*/
const addNewProps = (WrappedComponet, newProps) => {
return class WrappingComponet extends React.Component {
render () {
return (
<WrappedComponet {...this.props} {...newProps} />
)
}
}
}
2. 访问ref
已经好久没用了。。。不想研究。。pass
3. 抽取状态
react-redux 的 connect 函数执行的结果就是高阶组件
const WrappingComponet = connect(mapStateToProps,mapDispatchToProps)(WrappedComponet);
//WrappingComponet 继承了props
4. 包装组件
以上几种使用场景都是改变props, 此方式是改变 render 函数。
下述案例是 改变样式。 还可以组合多个 React 组件。
const styleHOC = (WrappedComponet, style) => {
return class WrappingComponet extends React.Component {
render () {
return (
<div style={style}>
<WrappedComponet {...this.props}/>
</div>
)
}
}
}
继承方式的高阶组件
继承方式的高阶组件采用继承关系关联作为参数的组件和返回的组件。
const addNewProps = (WrappedComponet, newProps) => {
return class WrappingComponet extends React.WrappedComponet {
render() {
this.props = {...this.props, ...newProps};
return super.render();
}
}
}
1. 操纵props
上述 demo 直接操纵 props 并不安全,可以利用 React.cloneElement 让组件重绘。
const modifyPropsHOC = (WrappedComponet, newProps) => {
return class WrappingComponet extends React.WrappedComponet {
render() {
const elements = super.render();
const newStyle = {
color: {elements && elements.type === 'div'} ? 'red' : 'green'
}
const _newProps = {...this.props, ...newProps, style: newStyle};
return React.cloneElement(elements, _newProps, lements.props.children);
}
}
}
继承方式的高阶组件唯一用得上的场景就是高阶组件需要根据参数组件 WrappedComponent 渲染结果来决定如何修改 props 。否则 推荐使用代理方式操纵 prop。
2. 操纵生命周期函数
因为继承方式的高阶函数返回的新组件继承了参数组件,所以可以重新定义任何一个 React 组件的生命周期函数 。这是继承方式高阶函数特用的场景,代理方式无法修改传入组件的生命周期函数,所以不具备这个功能 。
const onlyForLoggedinHOC = (WrappedComponent) => {
return class NewComponent extends WrappedComponent {
render() {
if (this.props.loggedin) {
return super.render();
} else {
return null
}
}
}
}
优先考虑组合,然后考虑继承
高阶组件的显示名
用于调试 debug 和日志清楚看到组件名。
HOCComponent.displayName = `Connect($(getDisplayName(WrappedComponent) )`
Connect(DemoComponent)
以函数为子组件
使用场景没啥约束,更为灵活。
const loggedinUser = 'mock user';
class AddUserProp extends React.Component {
render() {
const user = loggedinUser;
return this.props.children(user);
}
}
//使用
<AddUserProp>
{ (user) => <div>{user}</div> }
</AddUserProp>
<AddUserProp>
{ (user) => <Foo user={user}> }
</AddUserProp>
CountDown 案例
倒计时功能可以应用到很多场合中,基础就是简单的数字变化, 但是应用场景不同,其样式也不同,这个时候可以把基础功能提取出来。
//CountDown 核心代码
class CountDown extends React.Component {
constructor() {
super(...arguments);
this.state = {count: this.props.startCount};
}
/**
* [当CountDown组件装载完成时,通过setInterval函数启动每秒钟更新内部状态的操作]
* @Author zula
* @DateTime 2017-10-13
* @return {[type]} [description]
*/
componentDidMount() {
this.intervalHandle = setInterval( () => {
const newCount = this.state.count - 1;
if (newCount >= 0){
this.setState({count: newCount});
} else {
window.clearInterval(this.intervalHandle);
}
}, 1000)
}
/**
* [当组件销毁时,销毁定时器]
* @Author zula
* @DateTime 2017-10-13
* @return {[type]} [description]
*/
componentWillUnmount() {
if (this.intervalHandle) {
window.clearInterval(this.intervalHandle);
}
}
render() {
return this.props.children(this.state.count)
}
}
//使用
<CountDown startCount={10}>
{ (count) => <p>{count}</p> }
</CountDown>
<CountDown startCount={10}>
{ (count) => <p>{count > 0 ? count : '新年快乐'}</p> }
</CountDown>
- 成员变量和组件状态都是特定与某个组件实例的数据, 但是组件状态的改变可以引发组件的更新过程,而普通的成员变量则不会。