在React中通过反模式获取props中函数的更新
fi3ework opened this issue · comments
前言
在看 React 的内联函数和性能 看到了一段很有意思的代码段,乍一看挺简单的代码,但是弄懂还是认真的想了一下,在这里分享一下思考的过程。
代码
// 1. App 会传递一个 prop 给 From 表单
// 2. Form 将向下传递一个函数给 button
// 这个函数与它从 App 得到的 prop 相接近
// 3. App 会在 mounting 之后 setState,并传递
// 一个**新**的 prop 给 Form
// 4. Form 传递一个新的函数给 Button,这个函数与
// 新的 prop 相接近
// 5. Button 会忽略新的函数, 并无法
// 更新点击处理程序,从而提交陈旧的数据
class App extends React.Component {
state = { val: "one" }
componentDidMount() {
this.setState({ val: "two" })
}
render() {
return <Form value={this.state.val} />
}
}
const Form = props => (
<Button
onClick={() => {
submit(props.value)
}}
/>
)
class Button extends React.Component {
shouldComponentUpdate() {
// 让我们假装比较了除函数以外的一切东西
return false
}
handleClick = () => this.props.onClick()
render() {
return (
<div>
<button onClick={this.props.onClick}>这个的数据是旧的</button>
<button onClick={() => this.props.onClick()}>这个工作正常</button>
<button onClick={this.handleClick}>这个也工作正常</button>
</div>
)
}
}
在线把玩地址:这是一个运行该应用程序的沙箱
上面的三个 button。
第一个会打印 "one"。
第一个会打印 "two"。
第一个会打印 "two"。
奇怪,明明长得都差不多为什么会有区别呢?
解释
第一个
首先,从上到下看,App 的 state 更新,导致 re-render。Form 是一个 stateless component,接受一个新的 prop 必然会 re-render。然后是关键的 Button,Button 将 shouldComponent 给直接 return false 了,这会导致 render 不会被再次调用,在 Button 第一次 render 后(事实上也只有一个 render,因为 shouldComponentUpdate 直接 return false 了),onClick 指向的是 prevProps 的 this.props.onClick。
在这里还需要将 JSX 还原一下方便理解,JSX 调用 React.createElemennt 生成的 VDOM 的简化版可以表达为
{
type: button,
onClick: this.props.onClick,
children: "这个的数据是旧的",
...
}
此时,onClick 已经被赋值为了 prevProps.onClick 了,之后都不会再有任何改变。
prevProps.onClick 又是个什么样的函数呢?是这个样子的:
() => { submit(props.value) }
在第一次 render 传递给 Button 时,props.value 值为 "one"
,之后 Form re-render,会生成新的 onClick 函数传递给 Button,但是很遗憾,Button 内的 onClick 已经定死了,无法改变,所以总是会输出 "one"
。
第二次 & 第三次
第二次和第三次是一个道理,这里只说第二次。
第二次相比第一次,区别就是不是直接去执行 props.onClick,而是每次都包一个新的箭头函数,在每一次执行的时候都会去获取一个新的 this.props.onClick,这就是一切的关键,虽然 shouldComponentUpdate 为 false,但是新的 props 还是已经来了,可以通过 this.props 引用。
思考
这段代码对我们有什么启发吗?
文章中作者说可以写一个 PureComponentMinusHandlers 高阶组件,作用类似高阶组件,但是对类似 onClick 的 props 的更新函数不会触发 update(因为它们基本也不会变化),而只对数据类的 props 的变化进行 PureComponent 的 shallowCompare,这是一种 react 的优化方法。
通过之前的分析还可以玩出下面的花样:
主动拉取更新的子组件来进行性能优化:像上例中的第二种和第三种方法,将子组件的 shouldComponentUpdate 返回 false,然后在传入的 props 的 handler 外面包一层匿名函数,这样每次调用 handler 都会去访问最新的 this.props.handler 等“非计划更新的 props”(函数的 props),这些函数的 props 可以返回父组件的一些内部状态传递给子组件。如此一来,子组件就从单向状态流变成了子组件向父组件主动拉取。但这与 React 的单向数据理念相左,是属奇技淫巧。
写了这么久react发现有这样的行为,但大神,我对你后面的思考方面的知识不是很理解,可以看个具体的例子吗?
之前写的比较乱,又整理了一下哈