【问题标题】:ReactJS Array.push function not working in setStateReactJS Array.push 函数在 setState 中不起作用
【发布时间】:2017-04-24 11:17:38
【问题描述】:

到目前为止,我正在制作一个包含 3 个问题的原始测验应用程序,所有问题都是对或错。在我的handleContinue 方法中,调用了将用户输入从无线电表单推送到userAnswers 数组中。第一次运行handleContinue 运行良好,之后会抛出错误:Uncaught TypeError: this.state.userAnswers.push is not a function(…)

import React from "react"

export default class Questions extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      questionNumber: 1,
      userAnswers: [],
      value: ''
    }

    this.handleContinue = this.handleContinue.bind(this)
    this.handleChange = this.handleChange.bind(this)
  }

  //when Continue button is clicked
  handleContinue() {
    this.setState({
      //this push function throws error on 2nd go round
      userAnswers: this.state.userAnswers.push(this.state.value),
      questionNumber: this.state.questionNumber + 1
    //callback function for synchronicity
    }, () => {
      if (this.state.questionNumber > 3) {
        this.props.changeHeader(this.state.userAnswers.toString())
        this.props.unMount()
      } else {
        this.props.changeHeader("Question " + this.state.questionNumber)
      }
    })
    console.log(this.state.userAnswers)
  }

  handleChange(event) {
    this.setState({
      value: event.target.value
    })
  }

  render() {
    const questions = [
      "Blargh?",
      "blah blah blah?",
      "how many dogs?"
    ]
    return (
      <div class="container-fluid text-center">
        <h1>{questions[this.state.questionNumber - 1]}</h1>
        <div class="radio">
          <label class="radio-inline">
            <input type="radio" class="form-control" name="trueFalse" value="true" 
            onChange={this.handleChange}/>True
          </label><br/><br/>
          <label class="radio-inline">
            <input type="radio" class="form-control" name="trueFalse" value="false" 
            onChange={this.handleChange}/>False
          </label>
          <hr/>
          <button type="button" class="btn btn-primary" 
          onClick={this.handleContinue}>Continue</button>
        </div>
      </div>
    )
  }
}

【问题讨论】:

    标签: javascript arrays reactjs state


    【解决方案1】:

    Array.push 不返回新数组。尝试使用

    this.state.userAnswers.concat([this.state.value])
    

    这将返回新的 userAnswers 数组

    参考:array pusharray concat

    【讨论】:

    • 这个答案解决了问题,但并没有真正解释发生了什么。我建议查看我的答案,该答案更详细地解释了该问题。
    【解决方案2】:

    您应该将状态对象视为不可变对象,但是您需要重新创建数组以使其指向新对象,设置新项目,然后重置状态。

     handleContinue() {
        var newState = this.state.userAnswers.slice();
        newState.push(this.state.value);
    
        this.setState({
          //this push function throws error on 2nd go round
          userAnswers: newState,
          questionNumber: this.state.questionNumber + 1
        //callback function for synchronicity
        }, () => {
          if (this.state.questionNumber > 3) {
            this.props.changeHeader(this.state.userAnswers.toString())
            this.props.unMount()
          } else {
            this.props.changeHeader("Question " + this.state.questionNumber)
          }
        })
        console.log(this.state.userAnswers)
      }
    

    上述解决方案的另一种选择是使用.concat(),因为它本身会返回一个新数组。它相当于创建一个新变量,但代码要短得多。

    this.setState({
      userAnswers: this.state.userAnswers.concat(this.state.value),
      questionNumber: this.state.questionNumber + 1
    }
    

    【讨论】:

    • 这很好用,但每次运行handleContinue 时都声明一个新变量似乎不是最佳选择。虽然我可能是错的,但想法?
    • 你是对的,它确实会浪费内存,但它比直接改变状态可能出现的潜在竞争条件要好
    • 这不是一个很好的答案。虽然它在技术上有效,但仅使用 concat 会更短更清晰,the docs 建议这样做。
    • 这并不总是正常工作。我只是遇到了一个问题,即相同的方法在一个组件中起作用,而在另一个具有相同道具的组件上不起作用。最好改用concat
    【解决方案3】:

    Do not modify state directly!一般情况下试试avoid mutation

    Array.prototype.push() 就地改变数组。所以本质上,当你 pushsetState 中的一个数组时,你使用 push 改变了原始状态。由于push 返回的是新数组长度而不是实际数组,因此您将this.state.userAnswers 设置为一个数值,这就是为什么您在第二次运行时得到Uncaught TypeError: this.state.userAnswers.push is not a function(…),因为您不能@ 987654331@到一个号码。

    您需要改用Array.prototype.concat()。它不会改变原始数组,而是返回一个包含新连接元素的新数组。这就是您想要在setState 中执行的操作。您的代码应如下所示:

    this.setState({
      userAnswers: this.state.userAnswers.concat(this.state.value),
      questionNumber: this.state.questionNumber + 1
    }
    

    【讨论】:

      【解决方案4】:

      我找到了解决办法。这也适用于 splice 和其他人。假设我有一个状态是一系列汽车:

      this.state = {
        cars: ['BMW','AUDI','mercedes']
      };
      this.addState = this.addState.bind(this);
      

      现在,addState 是我将用来向我的数组添加新项目的方法。这应该是这样的:

        addState(){
      
      let arr = this.state.cars;
      arr.push('skoda');
      this.setState({cars: arr});
      }
      

      感谢 duwalanise,我找到了这个解决方案。我所要做的就是返回新数组以推送新项目。我很长一段时间都面临着这种问题。我会尝试更多的功能,看看它是否真的适用于所有通常不会的功能。如果有人对如何使用更简洁的代码实现这一点有更好的想法,请随时回复我的帖子。

      【讨论】:

        【解决方案5】:

        在以后的 React 版本中推荐的方法是在修改状态时使用更新器函数来防止竞争条件:

        this.setState(prevState => ({
          userAnswers: [...prevState.userAnswers, this.state.value] 
        }));
        

        【讨论】:

          【解决方案6】:

          当你想向它推送一些东西时,改变你的状态的正确方法是执行以下操作。假设我们已经定义了一个状态:

          const [state, setState] = useState([])
          

          现在我们要将以下对象推入状态数组。我们使用 concat 方法来实现这个操作:

          let object = {a: '1', b:'2', c:'3'}
          

          现在要将这个对象推入状态数组,请执行以下操作:

          setState(state => state.concat(object))
          

          您将看到您的状态填充了该对象。 concat 有效而 push 无效的原因如下

          Array.prototype.push() adds an element into original array and returns an integer which is its new array length.
          
          
          
          Array.prototype.concat() returns a new array with concatenated element without even touching in original array. It's a shallow copy.
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2022-01-20
            • 1970-01-01
            • 2020-12-31
            • 2020-07-22
            • 2015-08-02
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多