一、派生state常见应用问题

大部分应用派生 state 导致的问题,不外乎两个起因:1,间接复制 props 到 state 上;2,如果 props 和 state 不统一就更新 state

间接复制props到state

最常见的误会就是 getDerivedStateFromPropscomponentWillReceiveProps 只会在 props “扭转”时才会调用。实际上只有父级从新渲染时,这两个生命周期函数就会从新调用,不论 props 有没有“变动”。所以,在这两个办法内间接复制(_unconditionally_)props 到 state 是不平安的。这样做会导致 state 后没有正确渲染

class EmailInput extends Component {  state = {    email: this.props.email  };  render() {    return <input onChange={this.handleChange} value={this.state.email} />;  }  handleChange = event => {    this.setState({ email: event.target.value });  };    componentWillReceiveProps(nextProps) {    // Do not do this!    if (nextProps.email !== this.state.email) {        this.setState({ email: nextProps.email });    }  }}class Timer extends Component {  state = {    count: 0  };  componentDidMount() {    this.interval = setInterval(      () =>        this.setState(prevState => ({          count: prevState.count + 1        })),      1000    );  }  componentWillUnmount() {    clearInterval(this.interval);  }  render() {    return (      <Fragment>        <blockquote>请输出邮箱:</blockquote>        <EmailInput email="example@google.com" />        <p>          此组件每秒会从新渲染一次        </p>      </Fragment>    );  }}render(<Timer />, document.getElementById("root"));

state 的初始值是 props 传来的,当在 <input> 里输出时,批改 state。然而如果父组件从新渲染,咱们输出的所有货色都会失落,即便在重置 state 前比拟 nextProps.email !== this.state.email 依然会导致更新。
这个小例子中,应用 shouldComponentUpdate ,比拟 props 的 email 是不是批改再决定要不要从新渲染。然而在实践中,一个组件会接管多个 prop,任何一个 prop 的扭转都会导致从新渲染和不正确的状态重置。加上行内函数和对象 prop,创立一个齐全牢靠的 shouldComponentUpdate 会变得越来越难。而且 shouldComponentUpdate 的最佳实际是用于性能晋升,而不是改过不适合的派生 state

在 props 变动后批改 state

持续下面的示例,咱们能够只应用 props.email 来更新组件,这样能避免批改 state 导致的 bug

class EmailInput extends Component {  state = {    email: this.props.email  };  render() {    return <input onChange={this.handleChange} value={this.state.email} />;  }  handleChange = event => {    this.setState({ email: event.target.value });  };    componentWillReceiveProps(nextProps) {    // Do not do this!    if (nextProps.email !== this.props.email) {        this.setState({ email: nextProps.email });    }  }}

当初组件只会在 prop 扭转时才会扭转,然而依然有个问题。设想一下,如果这是一个明码输出组件,领有同样 email 的两个账户进行切换时,这个输入框不会重置(用来让用户从新登录)。因为父组件传来的 prop 值没有变动!

侥幸的是,有两个计划能解决这些问题。这两者的关键在于,任何数据,都要保障只有一个数据起源,而且防止间接复制它。咱们来看看这两个计划。

一、齐全可控组件

咱们都晓得React表单中有受控组件,那么什么是齐全可控组件呢,看下列示例你就明确了

function ControlledEmailInput(props) {  return (    <label>      Email: <input value={props.email} onChange={props.handleChange} />    </label>  );}class App extends Component {  state = {    draftEmail: 'some.email@test.com'  };  handleEmailChange = event => {    this.setState({ draftEmail: event.target.value });  };  resetForm = () => {    this.setState({      draftEmail: 'some.email@test.com'    });  };  render() {    return (      <Fragment>        <h1>此示例展现了什么是齐全可控组件</h1>        <ControlledEmailInput          email={this.state.draftEmail}          handleChange={this.handleEmailChange}        />        <button onClick={this.resetForm}>重置</button>      </Fragment>    );  }}render(<App />, document.getElementById("root"));

从上示例咱们就晓得了,这不正是咱们所见过的组件间通信嘛,子组件中的email齐全受父组件数据管制就像提线木偶一样

二、有key的齐全不受控组件

让子组件本人存储长期的state数据,子组件依然能够从 prop 接管“初始值”,然而更改之后的值就和 prop 没关系了

class EmailInput extends Component {  state = { email: this.props.defaultEmail };  handleChange = event => {    this.setState({ email: event.target.value });  };    resetForm = () => {    this.setState({ email: 'some.email@test.com' });  };  render() {    return <input onChange={this.handleChange} value={this.state.email} />;  }}class App extends Component {  inputRef = React.createRef();    state = {    draftEmail: 'some.email@test.com',  };    resetForm = () => {    this.inputRef.current.resetForm()  };  render() {    return (      <Fragment>        <h1>此示例展现了什么是有Key的非可控组件</h1>        <EmailInput          defaultEmail={this.state.draftEmail}          ref={this.inputRef}        />        <button onClick={this.resetForm}>重置</button>      </Fragment>    );  }}render(<App />, document.getElementById("root"));

总结

设计组件时,重要的是确定组件是受控组件还是非受控组件。
不要间接复制 props 的值到 state 中,而是去实现一个受控的组件,而后在父组件里合并两个值。
对于不受控的组件,当你想在 prop 变动时重置 state 的话,能够抉择一下几种形式:

  • 倡议: 重置外部所有的初始 state,应用 key 属性
  • 选项一:仅更改某些字段,察看非凡属性的变动(比方 props.userID)。
  • 选项二:应用 ref 调用实例办法。