简体   繁体   English

React.js setState 问题

[英]React.js setState issue

I am learning React.js and I have a very strange issue.我正在学习React.js ,但遇到一个非常奇怪的问题。 I follow the tutorial step by step, when I call this.setState to update the jokes array in this.state object with the modified one it doesn't change.我一步一步地按照教程进行操作,当我调用this.setState来更新this.state object 中的笑话数组时,修改后的数组不会改变。 I want to toggle the completed property to true / false on checkbox click.我想在单击复选框时将completed的属性切换为true / false There is no console error.没有控制台错误。

Any opinion is welcome.欢迎任何意见。

JokesTemplate.js JokesTemplate.js

function JokeTemplate(props){

    return (
      <div className="col-md-4">
        <label>Joke {props.item.id}</label>
        <p>Question {props.item.desc}</p>
        <p>Answer: {props.item.ans}</p>
        <input type="checkbox" checked={props.item.completed} onChange={() => props.handleChange(props.item.id)} />
      </div>
    );
}

export default JokeTemplate;

Jokes.js笑话.js

import React from 'react';

import JokeTemplate from './JokeTemplate';

const jokes = [{
    id:1,
    desc: "Question 1",
    ans: "Answer 1",
    completed: false
},
{
    id:2,
    desc: "Question 2",
    ans: "Answer 2",
    completed: true
},
{
    id:3,
    desc: "Question 3",
    ans: "Answer 3",
    completed: false
}];

class Jokes extends React.Component{
    constructor(){
      super()
      this.state = {
          jokesLst: jokes
      }

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

    handleChange(id){

       this.setState(prevState => {
           let updatedObj = prevState.jokesLst.map( item =>{
               if(item.id === id){ 
                   item.completed = !item.completed;
               }
               return item;
           })
           
           return { jokesLst: updatedObj }
       });

    }

    render(){

        const jokesComponentArr = this.state.jokesLst.map( joke => <JokeTemplate key={joke.id} item={joke} handleChange={this.handleChange} />);

        return (
            <>
              {jokesComponentArr}
            </>
        )
    }
    
}

export default Jokes;

App.js应用程序.js

import React from 'react';
import logo from './logo.svg';
import './App.css';

import NavBar from './components/NavBar';
import Jokes from './components/Jokes';

function App() {
  
  return (
   <div className="App">
      <NavBar />
      <header className="App-header">
        <Jokes />
      </header>
    </div>
  );
}

export default App;

Thanks in advance.提前致谢。

It seems your code still modifies the original elements in the array because they are objects which are not referenced by value.您的代码似乎仍在修改数组中的原始元素,因为它们是未按值引用的对象。 To eliminate the issue you need to copy each elements in your .map() call in your callback within prevState .要消除此问题,您需要在prevState的回调中复制.map()调用中的每个元素。

In handleChange you can try the following instead as:handleChange ,您可以尝试以下操作:

this.setState(prevState => {
    let updatedObj = prevState.jokesLst.map(item => {
        const newItem = { ...item }

        if (newItem.id === id) {
           newItem.completed = !newItem .completed
        }

        return newItem
    })
          
    return { jokesLst: updatedObj }
})

See the difference with the extra const newItem = {...item } line above.查看上面额外的const newItem = {...item }行的区别。

Or you can use it even shorter:或者你可以使用更短的时间:

this.setState(prevState => ({
   ...prevState,
   jokesLst: prevState.jokesLst.map(item => ({
      ...item,
      completed: item.id === id ? !item.completed : item.completed
   }))
})

I think all you need to modify is only at the handleChange() method inside Jokes.js我认为您需要修改的只是 Jokes.js 中的 handleChange() 方法

I have never seen such way of putting logic to filter "item" and modify its "completed" field inside of this.setState() method.我从未见过这种将逻辑用于过滤“项目”并在 this.setState() 方法内修改其“已完成”字段的方法。 But I would suggest you to do it this way.但我建议你这样做。 So here we create a new instance of updatedObj, do all the logic for manipulating the completed field.所以在这里我们创建了一个新的 updatedObj 实例,执行所有操作已完成字段的逻辑。 and then finally just call setState and pass the updatedObj to it directly.最后只需调用 setState 并将 updatedObj 直接传递给它。 I tested this on codesandbox, and it works.我在 codesandbox 上测试了这个,它可以工作。

handleChange(id) {
   let updatedObj = this.state.jokesLst.map((item) => {
      if (item.id === id) {
        item.completed = !item.completed;
      }
    
      return item;
    });
    
    this.setState({
      jokesLst: updatedObj
    });
   };

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM