【问题标题】:When is state available in componentWillReceiveProps?componentWillReceiveProps 中的状态何时可用?
【发布时间】:2016-03-11 22:24:13
【问题描述】:

我构建了一个文本输入组件,允许用户在文本框中输入数字。如果用户输入了一个有效的数字,该组件就会调用它的 onChange 属性,并将其状态设置为他们输入的字符串。这样如果用户输入“-”或“.”组件本身可以保留“-”或“.”。在文本框中,而用户输入他们的号码的其余部分。

当他们最终输入一个有效数字时,它会被解析并传递给onChange 属性。然后父级设置自己的内部状态,并将解析后的数字传递回 value 属性。

父级也可能出于用户输入以外的原因在value prop 中发送一个新数字,因此我需要实现componentWillReceiveProps 以在收到新值时用新值覆盖状态。

这导致了一个新问题:当用户输入“0”时。进入输入框,成功解析为数字0,通过onChangeprop传递给parent,再通过valueprop传回给child。然后组件在其上调用toString(),这会产生字符串“0”,它会覆盖“0”。并且无法输入带小数的数字。

为了解决这个问题,组件首先检查 value 属性中传递的值是否等于状态中的字符串,在解析之后(基本上,如果两个值在规范化之后相等)以及它们是否相等,它不会覆盖状态中的值,因此显示给用户的字符串将保持不变。

但是,在componentWillReceiveProps 内部,状态与旧版本的状态相同,并且不反映用户输入的新字符。操作似乎是按此顺序进行的:

  1. 用户键入一个字符。
  2. 调用字符回调。输入是一个有效数字。
  3. 组件调用 setState 来更新表示输入字符串的内部状态。
  4. 组件调用onChange prop 将新编号传递给父级。
  5. 父级设置自己的状态。
  6. Parent 将自己的状态传递回value prop(作为数字)中的组件。
  7. componentWillReceiveProps 被调用
  8. 应用来自组件的挂起状态更改。

问题是由 #7 发生在 #8 之前引起的(大概我还没有深入研究 React 源代码来验证这一点)。我已经通过在用户键入字符时直接在回调中修改this.state 以及使用具有相同值的setState 解决了这个问题,但我知道应该尽可能避免直接修改this.state

所以我的问题是:我可以在不直接修改this.state 的情况下解决这个问题吗?如果有,怎么做?

【问题讨论】:

  • 我不明白为什么您的输入需要调用toString。为什么您的父母不发送输入期望的值?在我看来,您的输入组件太聪明了,应该让父组件处理有关如何处理字符串值的任何逻辑。
  • 父级不存储字符串,它存储数字。
  • 但它当然可以......这似乎可以解决这个问题。
  • 在某些时候,该字符串需要变成一个数字。此外,该组件是允许用户选择颜色的较大组件的一部分,并且该组件具有在用户编辑时保持无效状态的相同问题。我认为在一般情况下应用该策略最终会导致大量重复的验证逻辑存在不同步的风险。
  • 我认为您应该发布一些代码或对此进行调整。将状态和逻辑移动到父容器中以避免此类问题是一种非常典型的 React 处理方式。如果有更多的事情阻止这种类型的重构,那么我们需要了解它。

标签: reactjs


【解决方案1】:

编辑:我刚刚意识到我完全错过了你问题的重点。 :|

你可以这样做:

const Form = React.createClass({
  getInitialState: function() {
    return {
      number: {
        txt: null, // the text that displays in the input box
        val: null  // the actual number value
      }
    };
  },

  componentWillReceiveProps: function(nextProps) {
    // assume that some `parseInput` function exists to get the number value
    let numberVal = parseInput(nextProps.number);
    if (numberVal !== this.state.number.val) {
      this.setState({
        number: {
          txt: numberVal,
          val: numberVal
        }
      });
    }
  },

  handleChange: function(newTxt) {
    // update number input
    let newNumberState = {
      txt: newTxt
    };
    // assume that some `parseInput` function exists to get the number value
    let newVal = parseInput(newTxt);
    if (newVal !== this.state.number.val) {
      newNumberState.val = newVal;
      tellSomeoneThatWeHaveANewNumber(newVal);
    }
    // update state (either txt or txt & val)
    this.setState({
      number: newNumberState
    });
  },

  render: function() {
    return (
      <NumberInput
        onInputChange={this.handleChange}
        value={this.state.number.txt}
      />
    );
  }
});

const NumberInput = React.createClass({
  handleChange: function(e) {
    // filter out unwanted characters
    const newValue = e.target.value.replace(/[^\d.-]/g, '');
    // send new value to parent component
    this.props.onInputChange(newValue);
  },

  render: function() {
    let {value} = this.props;
    return (
      <input
        onChange={this.handleChange}
        type="text"
        value={value || ''}
      />
    );
  }
});

【讨论】:

    猜你喜欢
    • 2016-11-09
    • 1970-01-01
    • 2018-06-10
    • 1970-01-01
    • 2016-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多