0

Im just starting with react and I get a weird behaviour that I am not able to understand.

  class App extends Component {

  state = {
    newTask: [
      {
      header: 'Header',
      description: 'Detailed description'
      }]}

  setTask = () => {

    console.log(this.state.newTask)

    this.setState({newTask: [
      {
      header: "some change",
      description: "some change again"
      }]});

    console.log(this.state.newTask)}

  render() {
    return (
      <div className="App">
      <h1>Hello There.</h1>
      <NewTaskModal callbackFromParent={this.setTask}/>
      <Tasks task={this.state.newTask}  />
      </div>
    );}}

export default App;

The App component is a parent to other component Tasks which is a parent to component Task. When I click button in NewTaskModal component, the setTask method is called and it should update the state. I have put console.logs around the block of code responsible for updating the state to see its current value.

Clicking the button for the first time console.logs in both cases the initialized value of the state BUT in Tasks component the Task is rendered with the new values ('some change'). After the clicking the button for the second time it then finally updates the state and console.logs the updated values.

My desired result is updating the state by not changing it but by adding more Objects of header and description to it. Also I would like to after clicking the button for the first time to update the state immediately not after the second click.

I have spent hours on this and I cant seem to find the right solution so I ask for help..

Thank you.

mosses
  • 351
  • 3
  • 18

2 Answers2

1

Reading state right after setState is incorrect, because:

setState() as a request rather than an immediate command to update the component. For better perceived performance, React may delay it, and then update several components in a single pass. React does not guarantee that the state changes are applied immediately.

setState() does not always immediately update the component. It may batch or defer the update until later. This makes reading this.state right after calling setState() a potential pitfall.

I guess setTask could be something like this:

setTask = (taskFromNewTaskModal) => {
  this.setState((state) => ({
    newTask: [
      ...state.newTask, taskFromNewTaskModal
    ]
  })
}

in the code above you'd have to pass a new task to setTask from you NewTaskModal, then add it to the list of the tasks.

Evgeny Timoshenko
  • 3,119
  • 5
  • 33
  • 53
  • 1
    As a sidenote, if you really want to access the state right after you call `setState`, you can use the second parameter of the `setState` function which allows you to send a callback method that will be called right after the state is updated. But use this with care. – César Landesa May 24 '18 at 12:28
  • the thing is I have two props (header and description) so I tried something like this `setTask = (header, description) => { console.log(header) console.log(description) console.log(this.state.newTask) this.setState({header: [...this.state.newTask, header]}) this.setState({description: [...this.state.newTask, description]}) }` I am not sure how to correctly target the header and description in state array object. – mosses May 24 '18 at 12:45
0

Change the setTask function as follows:

setTask = () => {
      console.log(this.state.newTask);
      let { newTask } = this.state;
      newTask.push({
             header: "some change",
            description: "some change again"
      });
    this.setState({ newTask });

     // console.log(this.state.newTask); // this can't work just after setting the state
   };

What I have done is: I read the newTask array from the state, append the new object with changes and set state with the updated newTask object.

N. Solomon
  • 140
  • 2
  • 7
  • I think state should not be mutated like this. – Evgeny Timoshenko May 24 '18 at 12:39
  • @EvgenyTimoshenko can you please more as to why it shouldn't be? – N. Solomon May 24 '18 at 12:46
  • mutating state is bug-prone, thou I cannot give an example why. in the code above `newTask` is a reference to an array from the state, so appending an item modifies the state. Take a look at this answer https://stackoverflow.com/questions/37755997/why-cant-i-directly-modify-a-components-state-really/37760774 – Evgeny Timoshenko May 24 '18 at 12:56