简体   繁体   中英

React.js setState issue

I am learning React.js and I have a very strange issue. 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. I want to toggle the completed property to true / false on checkbox click. There is no console error.

Any opinion is welcome.

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

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

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 .

In handleChange you can try the following instead as:

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.

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

I have never seen such way of putting logic to filter "item" and modify its "completed" field inside of this.setState() method. 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. and then finally just call setState and pass the updatedObj to it directly. I tested this on codesandbox, and it works.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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