一个函数执行的返回结果还是一个函数时,它就是一个高阶函数,如下
function debounce (fn, delay = 500, context) {
let timer = null
return function (...args) {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn.apply(context, args)
timer = null // 闭包释放
}, delay)
}
}
防抖函数执行后,返回一个新的函数,新函数内部通过闭包获取实参及变量timer
高阶组件(
HOC
)是React
中用于复用组件逻辑的一种高级技巧。HOC
自身不是React API
的一部分,它是一种基于React
的组合特性而形成的设计模式
函数执行后返回的是一个组件,该函数就是一个高阶组件,如下:
function logProps (WrappedComponent) {
return class extends React.Component {
componentDidUpdate(oldProps) {
console.log('oldProps: ', oldProps)
console.log('newProps: ', this.props)
}
render() {
return <WrappedComponent {...this.props} />
}
}
}
该函数的作用是,为一个组件打印props
(通过注册componentDidUpdate
钩子函数,在组件活动周期,父级传递的props
变化重新渲染组件,会执行该钩子函数),功能并不实用,仅举例子
使用HOC
可以很好地解决横切关注点问题
官网链接:https://react.docschina.org/docs/higher-order-components.html
使用高阶组件根据横切关注点分离代码,优化后如下:
// 评论列表
class CommontList extends React.Component {
render() {
return (
<ul>
{this.props.data.map(commont => (<li key={commont}>{commont}</li>))}
</ul>
)
}
}
// 博客文章
class BlogPost extends React.Component {
render() {
return (
<ul>
{this.props.data.text}
</ul>
)
}
}
function withSubscription(WrappedComponent, selectData) {
return class extends React.Component {
constructor(props) {
super(props)
this.handleDataChange = this.handleDataChange.bind(this)
this.state = {
data: selectData(DataSource, this.props)
}
}
componentDidMount() {
DataSource.addChangeListener(this.handleDataChange)
}
componentWillUnmount() {
DataSource.removeChangeListener(this.handleDataChange)
}
handleDataChange() {
console.log(`${WrappedComponent.name ? WrappedComponent.name : 'Component' } change!`)
this.setState({
data: selectData(DataSource, this.props)
})
}
render() {
return <WrappedComponent data={this.state.data} {...this.props} />
}
}
}
// 使用【订阅】高阶组件封装
const SubscribeBlogPost = withSubscription(
BlogPost,
(DataSource, props) => DataSource.getBlogPost(props.id))
const SubscribeCommontList = withSubscription(
CommontList,
DataSource => DataSource.getComments())
export default class Home extends React.Component {
state = {
blogId: 1,
commontInput: '',
}
componentDidMount () {
}
handleCommontInput(e) {
this.setState({
commontInput: e.target.value
})
}
addComment() {
DataSource.commonts = [...DataSource.getComments(), this.state.commontInput]
this.setState({
commontInput: ''
})
}
render () {
return (
<div>
<h1>Home</h1>
<div>
<input value={this.state.commontInput} onInput={(e) => this.handleCommontInput(e)} />
<button onClick={() => this.addComment()}>添加评论</button>
</div>
<SubscribeCommontList />
<hr />
<SubscribeBlogPost id={this.state.blogId} />
</div>
)
}
}
解释如下:
DataSource
是全局的数据源,一方面提供组件需要的数据,另一方面在数据变化后,执行通过DataSource.addChangeListener()
方法绑定的订阅更新函数,在数据变化时,DataSource
内部会自动调用所有的监听函数;定义了一个CommentList
组件和一个BlogPost
组件,它们都会接收一个data
属性(this.props.data
)而该属性的数据类型,则完全由高阶组件withSubscription
的第二个参数selectData
函数确定;高阶组件withSubscription
内部初始化会调用一次selectData()
获取数据,当数据源DataSource
变化后,DataSource trigger
所有的监听函数,也就是handleDataChange()
方法,setState({ data: selectData(DataSource, this.props}),更新了高阶组件的状态后,其被包裹的组件也会刷新
使用建议
高阶组件应当是一个纯函数,不要掺杂其他副作用不要改变原始组件,使用组合
高阶组件的分离横切关注点应该颗粒化更好,以方便组件的复用和维护更不要修改原始传入的组件特性,应当使用组合包装,例如:在上文【订阅】高阶组件中继续添加打印props
的功能
function logProps (WrappedComponent) {
return class extends React.Component {
componentDidUpdate(oldProps) {
console.log('oldProps: ', oldProps)
console.log('newProps: ', this.props)
}
render() {
return <WrappedComponent {...this.props} />
}
}
}
const LogPropsSubscribeBlogPost = logProps(withSubscription(
BlogPost,
(DataSource, props) => DataSource.getBlogPost(props.id)))
const LogPropsSubscribeCommontList = logProps(withSubscription(
CommontList,
DataSource => DataSource.getComments()))
上例需要打印props
是新的需求,虽然也可以在withSubscription
高阶组件内直接修改,毕竟不冲突,但是如果那样加了之后,**打印props
**就和【订阅】
功能绑定在一起了,所以最好分开;方便复用高阶组件,没有修改原组件
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)