什么时候用到派生
- 1,直接复制 props 到 state 上;
- 2,如果 props 和 state 不一致就更新 state。
如果你只是为了缓存(memoize)基于当前 props 计算后的结果的话, 请看 https://www.xmetal.cc/?p=1315
问题 :针对输入 组件
- 父组件更新, props 改变, 会重置 state ,我们输入的所有东西都会丢失
- if (nextProps.email !== this.props.email) {}只要 props.email 改变,就改变 state, 这样不会丢掉
- 上面会导致父组件格式如果是[{email:xxx, email:xxx}] , 子组件的input 就不能更新
设计组件时,重要的是确定组件是受控组件还是非受控组件
不要直接复制(mirror) props 的值到 state 中,而是去实现一个受控的组件,然后在父组件里合并两个值。比如,不要在子组件里被动的接受 props.value 并跟踪一个临时的 state.value,而要在父组件里管理 state.draftValue 和 state.committedValue,直接控制子组件里的值。这样数据才更加明确可预测
建议:完全可控的组件
function EmailInput(props) {
return <input onChange={props.onChange} value={props.email} />;
}
建议:有 key 的非可控组件
在这密码管理器的例子中,为了在不同的页面切换不同的值,我们可以使用 key 这个特殊的 React 属性。当 key 变化时, React 会创建一个新的而不是更新一个既有的组件。 Keys 一般用来渲染动态列表,但是这里也可以使用
class EmailInput extends Component {
state = { email: this.props.defaultEmail };
handleChange = event => {
this.setState({ email: event.target.value });
};
render() {
return <input onChange={this.handleChange} value=
{this.state.email} />;
}
}
//每次 ID 更改,都会重新创建 EmailInput ,并将其状态重置为最新的 defaultEmail 值。
<EmailInput
defaultEmail={this.props.user.email}
key={this.props.user.id}
/>
选项一:用 prop 的 ID 重置非受控组件
如果某些情况下 key 不起作用(可能是组件初始化的开销太大),一个麻烦但是可行的方案是在 getDerivedStateFromProps 观察 userID 的变化:
class EmailInput extends Component {
state = {
email: this.props.defaultEmail,
prevPropsUserID: this.props.userID
};
//使用了 getDerivedStateFromProps,用 componentWillReceiveProps 也一样。
static getDerivedStateFromProps(props, state) {
// 只要当前 user 变化,
// 重置所有跟 user 相关的状态。
// 这个例子中,只有 email 和 user 相关。
if (props.userID !== state.prevPropsUserID) {
return {
prevPropsUserID: props.userID,
email: props.defaultEmail
};
}
return null;
}
// ...
}
选项二:使用实例方法重置非受控组件
更少见的情况是,即使没有合适的 key,我们也想重新创建组件。一种解决方案是给一个随机值或者递增的值当作 key,另外一种是用实例方法强制重置内部状态:
refs 在某些情况下很有用,比如这个。但通常我们建议谨慎使用。即使是做一个演示,这个命令式的方法也是非理想的,因为这会导致两次而不是一次渲染。
export default class AccountsList extends Component {
inputRef = React.createRef();
state = {
selectedIndex: 0
};
handleChange = index => {
this.setState({ selectedIndex: index }, () => {
const selectedAccount = this.props.accounts[index];
this.inputRef.current.resetEmailForNewUser(selectedAccount.email);
});
};
render() {
const { accounts } = this.props;
const { selectedIndex } = this.state;
const selectedAccount = accounts[selectedIndex];
return (
<Fragment>
<UncontrolledEmailInput
defaultEmail={selectedAccount.email}
ref={this.inputRef}
/>
<p>
Accounts:
{this.props.accounts.map((account, index) => (
<label key={account.id}>
<input
type="radio"
name="account"
checked={selectedIndex === index}
onChange={() => this.handleChange(index)}
/>{" "}
{account.name}
</label>
))}
</p>
</Fragment>
);
}
}
//子组件
class EmailInput extends Component {
state = {
email: this.props.defaultEmail
};
resetEmailForNewUser(newEmail) {
this.setState({ email: newEmail });
}
// ...
}