删除项目后反应列表呈现错误数据

React list rendering wrong data after deleting item

我有一个简单的学生对象列表,其中包含名称和他们在状态中的分数。

他们的名字绑定到<b>{student.name}</b>,他们的分数绑定到

<input type="text" defaultValue={student.score}/>

每当我想从这个列表中删除第一个学生并且 通过调用 set state 重新渲染组件。

第二个学生的输入标签显示第一个学生的分数而不是自己的。为什么会发生在我做错的地方??

Here is my code jsbin

class App extends React.Component{
  constructor(){
    super();
    this.state ={
      students:[{name:"A",score:10},{name:"B",score:20},{name:"C",score:30}]
    }
  }
  
  onDelete(index){
    this.state.students.splice(index,1);
    this.setState(this.state);
  }
  
   render(){
     return(
       <div>
         {this.state.students.map((student,index)=>{
              return(
                <div key={index}>
                    <b>{student.name}</b> - <input type="text" defaultValue={student.score}/>
                     <button onClick={this.onDelete.bind(this,index)}>delete</button>
                </div>                
              )
         })}
       </div>
     )
   }
}


ReactDOM.render(<App/>,document.getElementById("main"));

这是因为您使用的是 key={index} 而不是学生独有的值。

当数组被修改时,删除索引后的学生将有错误的键,React 不会注册键更改以使用更新的数据重新呈现。

你应该改用这样的东西...

<div key={student.name}>

假设 student.name 是唯一的。

最好使用唯一 ID 作为键而不是索引。如果您的 JSON 没有提供唯一的 ID,您可以使用类似 uuid 的 npm 模块为您生成它。这是linkhttps://www.npmjs.com/package/uuid

然后您可以将其导入并使用,如下所示

import { v4 } from 'uuid'; //there are 5 versions. You can use any.

然后像下面这样调用v4函数用它来生成uique Id

id={v4()}

最佳实践

老实说,您不应该直接改变状态数据。你应该克隆然后像这样做你的任务

onDelete(index) {
    const newStudents = [...this.state.students];
    newStudents.splice(index, 1);
    this.setState({ students: newStudents });
}

The previous state will be polluted with your mutation. Due to which, the shallow compare and merge of two states will be disturbed or won't happen, because you'll have only one state now. This will disrupt all the React's Lifecycle Methods.


您的问题和解决方案

  1. 如果您使用输入 readOnly,您应该将 defaultValue 更改为 value ,这样您的代码才能正常工作。
<input type="text" value={student.score} readOnly />

class App extends React.Component {
  constructor() {
    super();
    this.state = {
      students: [
        { name: "A", score: 10 },
        { name: "B", score: 20 },
        { name: "C", score: 30 }
      ]
    };
  }

  onDelete(index) {
    const newStudents = [...this.state.students];
    newStudents.splice(index, 1);
    this.setState({ students: newStudents });
  }

  render() {
    return (
      <div>
        {this.state.students.map((student, index) => {
          return (
            <div key={index}>
              <b>{student.name}</b> -{" "}
              <input type="text" value={student.score} readOnly />
              <button onClick={this.onDelete.bind(this, index)}>delete</button>
            </div>
          );
        })}
      </div>
    );
  }
}

ReactDOM.render(<App/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>

<div id="app"></div>

  1. 否则,您应该提供唯一的 key。如果student.name不是唯一的,你可以这样随机GUID
const getGUID = () => "id" + Math.random().toString(16).slice(2);
>  <div key={getGUID()}>

const getGUID = () => "id" + Math.random().toString(16).slice(2);

class App extends React.Component {
  constructor() {
    super();
    this.state = {
      students: [
        { name: "A", score: 10 },
        { name: "B", score: 20 },
        { name: "C", score: 30 }
      ]
    };
  }

  onDelete(index) {
    const newStudents = [...this.state.students];
    newStudents.splice(index, 1);
    this.setState({ students: newStudents });
  }

  render() {
    return (
      <div>
        {this.state.students.map((student, index) => {
          return (
            <div key={getGUID()}>
              <b>{student.name}</b> -{" "}
              <input type="text" defaultValue={student.score} />
              <button onClick={this.onDelete.bind(this, index)}>delete</button>
            </div>
          );
        })}
      </div>
    );
  }
}

ReactDOM.render(<App/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>

<div id="app"></div>

但是,在这种情况下,不稳定的键将 cause harmful performance 因为组件实例 - Virtual DOM & DOM 节点 - actual DOM 总是不必要地重新创建。

总之,根据你的数据和行为,你可以选择正确的方式来完成。