可能用不到派生 state ,可控 组件,非可控组件

什么时候用到派生

  • 1,直接复制 props 到 state 上;
  • 2,如果 props 和 state 不一致就更新 state。

如果你只是为了缓存(memoize)基于当前 props 计算后的结果的话, 请看 https://www.xmetal.cc/?p=1315

问题 :针对输入 组件

  1. 父组件更新, props 改变, 会重置 state ,我们输入的所有东西都会丢失
  2. if (nextProps.email !== this.props.email) {}只要 props.email 改变,就改变 state, 这样不会丢掉
  3. 上面会导致父组件格式如果是[{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 });
  }

  // ...
}

Leave a Reply

Your email address will not be published. Required fields are marked *