You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中使用this.setState时如何避免修改原始JSON数据顺序

解决React中排序修改原始JSON数据的问题

这个问题的核心原因是JavaScript数组是引用类型,而且sort()方法是原地排序——它会直接修改调用它的原数组。你把原始的test JSON数组直接赋值给了state,之后对this.state.test调用sort()时,实际上是在修改原始的JSON数组(因为它们指向同一个内存地址)。

下面是具体的修复方案:

1. 初始化state时拷贝原始数组

首先,在组件的constructor里,不要直接将原始test数组赋值给state,而是创建一个浅拷贝,这样state里的数组和原始JSON数组就会是两个独立的引用:

constructor() {
  super();
  // 用扩展运算符创建数组拷贝,也可以用 test.slice()
  const testCopy = [...test];
  this.state = {
    index: 0,
    result: 0,
    testLength: test.length,
    test: testCopy
  };
}

2. 排序时始终操作拷贝后的数组

在getTest方法里,不要直接对this.state.test调用sort()(这会修改state里的数组本身,虽然不会影响原始JSON,但不符合React state不可直接修改的原则),而是先创建数组的拷贝,再对拷贝排序。同时,对于每个问题的answer数组,也要做同样的处理——因为数组里的对象是引用类型,直接排序item.answer会修改state里对应对象的answer数组:

getTest() {
  // 先拷贝state里的test数组,再排序
  const sortedTest = [...this.state.test].sort((a, b) => 0.5 - Math.random());
  
  return sortedTest.map((item, i) => {
    if (i == 0) {
      // 拷贝answer数组再排序
      const sortedAnswers = [...item.answer].sort((a, b) => 0.5 - Math.random());
      return (
        <section key={i} className="d-flex main-sec">
          <h3>{item.question}</h3>
          <form className="list-group form">
            {sortedAnswers.map((ansItem, ansIndex) => {
              return (
                <li key={ansIndex} className="list-group-item test-list list-group-item-action">
                  <input type="radio" name="answer" value={ansItem.isTrue} onClick={this.onChange.bind(this)} />
                  <p>{ansItem.ans}</p>
                </li>
              );
            })}
          </form>
          <button className="pre-button btn btn-lg btn-danger" onClick={this.prevEl.bind(this)}>
            <i className="fa fa-long-arrow-left" aria-hidden="true"/> Prev
          </button>
          <button className="next-button btn btn-lg btn-success" onClick={this.nextEl.bind(this)}>
            Next <i className="fa fa-long-arrow-right" aria-hidden="true" />
          </button>
        </section>
      );
    } 
    else if(i == this.state.testLength - 1) {
      const sortedAnswers = [...item.answer].sort((a, b) => 0.5 - Math.random());
      return (
        <section key={i} className="d-none">
          <h3>{item.question}</h3>
          <form className="list-group form">
            {sortedAnswers.map((ansItem, ansIndex) => {
              return (
                <li key={ansIndex} className="list-group-item test-list list-group-item-action">
                  <input type="radio" name="answer" value={ansItem.isTrue} onClick={this.onChange.bind(this)} />
                  <p>{ansItem.ans}</p>
                </li>
              );
            })}
          </form>
          <button className="pre-button btn btn-lg btn-danger" onClick={this.prevEl.bind(this)}>
            <i className="fa fa-long-arrow-left" aria-hidden="true"/> Prev
          </button>
          <div id="check-result">
            <button className="pre-button btn btn-lg btn-success" onClick={this.prevEl.bind(this)}>
              Check Your Result
            </button>
          </div>
        </section>
      )
    } else {
      // 这里如果需要排序answer,同样要先拷贝;不需要排序的话,拷贝也能避免意外修改
      const sortedAnswers = [...item.answer];
      return (
        <section key={i} className="d-none">
          <h3>{item.question}</h3>
          <form className="list-group form">
            {sortedAnswers.map((ansItem, ansIndex) => {
              return (
                <li key={ansIndex} className="list-group-item test-list list-group-item-action">
                  <input type="radio" name="answer" value={ansItem.isTrue} onClick={this.onChange.bind(this)} />
                  <p>{ansItem.ans}</p>
                </li>
              );
            })}
          </form>
          <button className="pre-button btn btn-lg btn-danger" onClick={this.prevEl.bind(this)}>
            <i className="fa fa-long-arrow-left" aria-hidden="true" /> Prev
          </button>
          <button className="pre-button btn btn-lg btn-success" onClick={this.nextEl.bind(this)}>
            Next <i className="fa fa-long-arrow-right" aria-hidden="true" />
          </button>
        </section>
      );
    }
  });
}

为什么这样有效?

  • 扩展运算符[...arr]会创建一个新的数组实例,新数组和原数组共享里面的对象引用,但数组本身是独立的——排序操作只会影响新数组,不会修改原数组。
  • 这样既保证了你的随机排序需求,又不会触碰原始的JSON数据,同时也遵循了React中“不要直接修改state”的最佳实践。

内容的提问来源于stack exchange,提问作者Alisher Musurmonv

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:06:12