React组件props动态数据不更新及函数式props取值异常问询
先来说说你遇到的第一个核心问题:为什么父组件状态变化后,子组件MyComponent的数据不更新?
你的MyComponent是类组件,在constructor里把传入的props.textData和props.funcData赋值给了实例变量this._statData和this._dynData——但这里有个关键的React类组件特性:constructor只会在组件第一次挂载时执行一次。后续父组件传递新的props(因为状态变化导致的),constructor不会再运行,所以这两个实例变量会一直停留在初始化时的初始值,完全不会同步新的props。
哪怕父组件状态变化触发了MyComponent的render(因为props确实更新了),你在render里用的还是旧的实例变量,而不是最新的this.props,所以显示的内容自然不会变。
解决方法:
最直接也最推荐的方式,就是直接在render中使用this.props,不要存在实例变量里:
class MyComponent extends React.Component { render() { console.log(`Render MyComponent with static=${this.props.textData} dynamic=${this.props.funcData()}`) return ( <Text>static: {this.props.textData} / dyn: {this.props.funcData()}</Text> ) } }
这样每次render都会读取最新的props值,父组件状态变化后,子组件自然会同步更新。
如果因为某些业务需求,必须把props存在实例变量里,那就要用componentDidUpdate生命周期来监听props变化,手动更新实例变量:
class MyComponent extends React.Component { constructor(props) { super(props) this._statData = props.textData; this._dynData = props.funcData; } componentDidUpdate(prevProps) { // 当textData变化时更新实例变量 if (prevProps.textData !== this.props.textData) { this._statData = this.props.textData; } // 当funcData变化时更新实例变量 if (prevProps.funcData !== this.props.funcData) { this._dynData = this.props.funcData; } } render() { console.log(`Render MyComponent with static=${this._statData} dynamic=${this._dynData()}`) return ( <Text>static: {this._statData} / dyn: {this._dynData()}</Text> ) } }
接下来解答你的第二个疑问:为什么传递()=>funcCaption时总是返回初始值,而()=>Math.random()每次都能生成新值?
这是JavaScript闭包的特性在搞鬼:
- 当
Container第一次render时,会创建一个函数()=>funcCaption,这个函数会捕获当前作用域下的funcCaption变量(也就是初始值"Functional")。 - 因为你的
MyComponent只在初始化时把这个函数存到了this._dynData里,后续哪怕Container状态更新后传递了新的()=>funcCaption函数(这个新函数捕获的是最新的funcCaption),但MyComponent没有更新this._dynData(constructor不执行,也没做componentDidUpdate处理),所以你调用的永远是第一次的那个旧函数,它捕获的funcCaption自然永远是初始值。 - 而
()=>Math.random()不一样,这个函数不依赖任何外部变量,每次调用它都会直接执行Math.random()生成新值——哪怕是旧函数,每次调用的结果都是独立的,所以看起来每次都变。
只要你改成直接用this.props.funcData(),每次调用的都是父组件最新传递过来的函数,这个函数是Container当前render时创建的,捕获的是最新的funcCaption状态,就能拿到正确的值了。
内容的提问来源于stack exchange,提问作者atlascoder

