0

I am getting into reactjs and building a simple Todo app like shown below. However when I try to toggle checkbox they don't toggle and also no compile error is shown.

enter image description here

1. Used the following command to create app  
command: npx create-react-app test-app

* Folder Structure
  - node_modules
  - publics
  - src
     - App.css
     - App.js
     - App.test.js
     - index.css
     - index.js
     - TodoItem.js --
     - todosData.js --
     - ....

I have made changes to only 3 files. They are App.js, TodoItems.js, todosData.js. All of the code in pasted blow.

App.js

import React from "react"
import TodoItem from "./TodoItem"
import todosData from "./todosData"

class App extends React.Component {
    constructor() {
        super()
        this.state = {
            todos: todosData
        }
        this.handleChange = this.handleChange.bind(this)
    }
    
    handleChange(id) {
        this.setState(prevState => {
            const updatedTodos = prevState.todos.map(todo => {
                if (todo.id === id) {
                    todo.completed = !todo.completed
                }
                return todo
            })
            return {
                todos: updatedTodos
            }
        })
    }
    
    render() {
        const todoItems = this.state.todos.map(item => <TodoItem key={item.id} item={item} handleChange={this.handleChange}/>)
        
        return (
            <div className="todo-list">
                {todoItems}
            </div>
        )    
    }
}

export default App

TodoItems.js

import React from "react"

function TodoItem(props) {
    return (
        <div className="todo-item">
            <input 
                type="checkbox" 
                checked={props.item.completed} 
                onChange={() => props.handleChange(props.item.id)}
            />
            <p>{props.item.text}</p>
        </div>
    )
}

export default TodoItem

todosData.js

const todosData = [
    {
        id: 1,
        text: "Take out the trash",
        completed: true
    },
    {
        id: 2,
        text: "Grocery shopping",
        completed: false
    },
    {
        id: 3,
        text: "Clean gecko tank",
        completed: false
    },
    {
        id: 4,
        text: "Mow lawn",
        completed: true
    },
    {
        id: 5,
        text: "Catch up on Arrested Development",
        completed: false
    }
]

export default todosData

3 Answers 3

1

Here's a quick working snippet illustrating a functional implementation of your setup.

It passes a callback to setState and uses Array.find() to get a reference to the todo that we want to change based on the passed id.

<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
<div id="App"></div>
<script type="text/babel">
  const { useState} = React;
  const todosData = [
    {
        id: 1,
        text: "Take out the trash",
        completed: true
    },
    {
        id: 2,
        text: "Grocery shopping",
        completed: false
    },
    {
        id: 3,
        text: "Clean gecko tank",
        completed: false
    },
    {
        id: 4,
        text: "Mow lawn",
        completed: true
    },
    {
        id: 5,
        text: "Catch up on Arrested Development",
        completed: false
    }
];

function App() {
    const [todos, setTodos] = useState(todosData);

    const handleChange = (id) => {
      setTodos(prevTodos => {
        const todo = prevTodos.find(todo => todo.id === id);
        todo.completed = !todo.completed;
        return [...prevTodos];
      });
    }
  
    return (
      <div className="todo-list">
        {todos.map(item => <TodoItem key={item.id} item={item} handleChange={handleChange} />)}
      </div>
    )    
}

function TodoItem({item, handleChange}) {
    return (
        <div className="todo-item">
            <input 
                type="checkbox" 
                checked={item.completed} 
                onChange={() => handleChange(item.id)}
            />
            <p>{item.text}</p>
        </div>
    )
}

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

 </script>

Sign up to request clarification or add additional context in comments.

Comments

1

You are not passing the actual input/checkbox value to the state, which simplifies your work as against implementing the value change yourself.

import React from "react"

function TodoItem(props) {
    return (
        <div className="todo-item">
            <input 
                type="checkbox" 
                checked={props.item.completed} 
                onChange={val=> props.handleChange(props.item.id, val.target.checked)}
            />
            <p>{props.item.text}</p>
        </div>
    )
}

export default TodoItem

Also make a slight change to the handleChange to take both the id and value

handleChange(id, val) {
        this.setState(prevState => {
            const updatedTodos = prevState.todos.map(todo => {
                if (todo.id === id) {
                    todo.completed = val
                }
                return todo
            })
            return {
                todos: updatedTodos
            }
        })
    }

Comments

1

You can handle you state like this

handleChange = (id) => {
      const currState = {
         ...this.state,
      };
      const upgratedTodo = currState.todos.map((todo) => {
         if (todo.id === id) {
            todo.completed = !todo.completed;
         }
         return todo;
      });
      this.setState({
        todos: upgratedTodo,
      })
   };

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.