0

I'm beginner with react and making frontend applications, so I decide to learn with this youtube react tutorial. I have some knowlage about html, js, and css.

Link leads to lesson (exercise about state in React) where I encounter a problem. The problem is:

onChange function assigned to check box, when starting app by: npm start, is acting... strange. Same function works as expected if I do: npm run build, serve -s build.

In tutorial everything works fine, i've checked my code and it's 100% same as in tutorial. I've also send my source code to one of my colleagues and ... everything works as in tutorial (we both have Windows 10 installed).

When trying to launch app by serve -s build, i encourage another problem which i solved with help of this question (execution of scripts is disabled on this system - I've run Set-ExecutionPolicy RemoteSigned in windows powerShell).

I've also tried deleting node_modules and run npm install.

App.js (I've deleted imports and export of App.js):

class App extends React.Component {
  constructor() {
    super()
    this.state = {
      todos: checklist
    }
    this.handleChange = this.handleChange.bind(this)
  } 
  handleChange(id) {
    console.log("debug1", id)
    this.setState(prevState => {
      const updatedToDos = prevState.todos.map(todo => {
        if (todo.id === id) {
          console.log(todo.id, id, todo.isChecked)
          todo.isChecked = !todo.isChecked
          console.log(todo.id, id, todo.isChecked)
        }
        console.log(todo)
        return todo
      })
      console.log(updatedToDos)
      return {
        todos: updatedToDos
      }
    })
  }
  render() {
    const todoChecklist = this.state.todos.map(item => <ToDoItm key={item.id} item={item} 
      handleChange={this.handleChange.bind(this)}/>)
    return(
      <div>
        {todoChecklist}
      </div>
    )
  }
}

checklist.js is json file with collection of TodoItems

ToDoItm (also export and imports deleted):

function ToDoItm(props) {
    return (
        <div className="todo-item">
            <h1>{props.item.line}</h1>
            <input 
                type="checkbox" 
                checked={props.item.isChecked}
                onChange={() => props.handleChange(props.item.id)} 
            />
        </div>
    )
}

PS. Acting strange i mean - this what happends when i click first checkBox: clicking first checkbox

HoneyBunny
  • 33
  • 4

2 Answers2

1

So Basically here you are mututing original Object of react State which is not the right practice what you need to do is clone those object in this todos array.You may follow below code.Also this handleChange whole is not optimized it could be written in more efficient way but for beginner it is okay please read this https://daveceddia.com/why-not-modify-react-state-directly/

import React from 'react';

function ToDoItm(props) {
  return (
    <div className="todo-item">
      <h1>{props.item.line}</h1>
      <input
        type="checkbox"
        checked={props.item.isChecked}
        onChange={() => props.handleChange(props.item.id)}
      />
    </div>
  )
}

class App extends React.Component {
  constructor() {
    super()
    this.state = {
      todos: [{ id: 1, line: '1' }, { id: 2, line: '2' }, { id: 3, line: '3' }, { id: 4, line: '4' }]
    }
    this.handleChange = this.handleChange.bind(this)
  }
  handleChange(id) {
    console.log("debug1", id)
    this.setState(prevState => {
      const updatedToDos = prevState.todos.map(todo => {
        // change that i made to clone object inside todos array
        const newtodo = Object.assign({}, todo);
        if (newtodo.id === id) {
          console.log(newtodo.id, id, newtodo.isChecked)
          newtodo.isChecked = !newtodo.isChecked
          console.log(newtodo.id, id, newtodo.isChecked)
        }
        console.log(todo)
        return newtodo
      })
      console.log(updatedToDos)
      return {
        todos: updatedToDos
      }
    })
  }
  render() {
    const todoChecklist = this.state.todos.map(item => <ToDoItm key={item.id} item={item}
      handleChange={this.handleChange.bind(this)} />)
    return (
      <div>
        {todoChecklist}
      </div>
    )
  }
}

export default App;

Your see how in handleChange function i clone those object and return a newObject that has no reference to Original React State you may use different method to clone the inner object.

check if your main App is not wrapped around strict mode

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);  

StrictMode calls your App's constructor and other methods twice (just in development) to ensure that there are no side effects. Try to remove the StrictMode and see if it helps.

  • Yep it works, but problem is not solved compleatly because this method also gives me this strange effect of executing this map function twice when only clicking once - see screenshot - this is only one click on first checkbox. When building App with `npm run build` console shows only logs of 4 objects. – HoneyBunny Sep 01 '20 at 19:25
  • Try Removing StrictMode if its present – Hassan Raza Sep 02 '20 at 13:17
  • Yes, as I said in my answer I managed to solve this issue by deleting React.StrictMode component wrapper – HoneyBunny Sep 03 '20 at 08:24
0

So, I found an answer on this blog post.

It turns out that when you creating react app with: npx create-react-app the App.js component when calling ReactDOM.Render with it - is wraped with React.StrictMode

ReactDOM.render(
  <React.StrictMode>
    <App />,
  </React.StrictMode>,
  document.getElementById('root')
);

The release notes about this wrapper says:

React.StrictMode is a wrapper to help prepare apps for async rendering

React.StrictMode was source of re-rendering application twice and in consequence was source of the problem.

On the blog post you can read that:

One of the benefits that we get from React.StrictMode usage, is that it helps us to detect unexpected side effects in the render-phase lifecycles.

but lifecycles exceed my present knowlage about React.

Also you can read React.StrictMode documentation.

Deleting this wrapper solved this problem.

HoneyBunny
  • 33
  • 4