单向数据流
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。
额外的,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。
Props 的只读性
决不能修改自身的 props。所有组件都必须保护它们的 props 不被更改。
State 与 props 类似,但是 state 是私有的,并且完全受控于当前组件。state 允许组件随用户操作、网络响应或者其他变化而动态更改输出内容。
让组件在 props 变化时更新 state
在 vue 中我们会这样做,定义一个本地的 data 属性并将这个 prop 用作其初始值,然后通过 watch 监控 prop 变化,然后重复赋值给本地的 data 属性。
props: ['initialCounter'],
data () {
return {
counter: this.initialCounter
}
},
watch: {
initialCounter (newCounter) {
if (newCounter !== this.counter) {
this.counter = newCounter
}
}
}
在 react 中,从 16.3 版本开始,当 props 变化时,建议使用新的 static getDerivedStateFromProps
生命周期更新 state。创建组件以及每次组件由于 props 或 state 的改变而重新渲染时都会调用该生命周期:
class ExampleComponent extends React.Component {
// 在构造函数中初始化 state,
// 或者使用属性初始化器。
state = {
counter: this.props.initialCounter,
};
static getDerivedStateFromProps(props, state) {
if (props.initialCounter !== state.counter) {
return { counter: props.initialCounter };
}
// 返回 null 表示无需更新 state。
return null;
}
handleClick = () => {
// 点击之后无法修改 counter 的 bug
this.setState({counter: this.state.counter + 1})
}
render() {
return (
<div onClick={this.handleClick}>{this.state.counter}</div>
);
}
}
getDerivedStateFromProps
会在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。它应返回一个对象来更新 state,如果返回 null 则不更新任何内容。
请注意,不管原因是什么,都会在每次渲染前触发此方法。这与 componentWillReceiveProps
形成对比,后者仅在父组件重新渲染时触发,而不是在内部调用 setState 时。
基于 props 更新 state,旧的 componentWillReceiveProps
和新的 getDerivedStateFromProps
方法都会给组件增加明显的复杂性。这通常会导致 bug。
最常见的误解就是 getDerivedStateFromProps
和 componentWillReceiveProps
只会在 props “改变”时才会调用。实际上只要父级重新渲染时,这两个生命周期函数就会重新调用,不管 props 有没有“变化”。所以,在这两个方法内直接复制 props 到 state 是不安全的。这样做会导致 state 后没有正确渲染。
希望以上能解释清楚为什么直接复制 prop 到 state 是一个非常糟糕的想法。
虽然这个设计就有问题,但是这样的错误很常见,(我就犯过这样的错误)。任何数据,都要保证只有一个数据来源,而且避免直接复制它。
编写完全可控的组件
阻止上述问题发生的一个方法是,从组件里删除 state。然后传入在父组件中定义的处理函数进行修改。
class ExampleComponent extends React.Component {
render() {
return (
<div onClick={this.props.handleClick}>
{this.props.initialCounter}
</div>
);
}
}
虽然 vue 中没有这个问题,但是建议大家不要在组件里面修改 props,任何数据,都要保证只有一个数据来源,而且避免直接复制它。都必须保护它们的 props 不被更改。