I am new to react and I am making a simple todo app using react js and material ui. What I have is a separate component for taking the user input (TodoInput), and a separate component for rending each individual todo task (TodoCards). What I want to do is enable the user to click the checkbox rendered in the TodoCards component once they have completed the task. I ran into an issue where when a single checkbox is clicked, all the checkboxes for every card component is checked. I’m unsure to as why this is happening, any guidance or explanation towards the right direction would be greatly appreciated.
TodoInput.js
import React, { useState } from 'react'; import { makeStyles } from '@material-ui/core/styles'; import { TextField, Button } from '@material-ui/core'; import { TodoCards } from '../UI/TodoCards'; import { Progress } from '../UI/Progress'; const useStyles = makeStyles((theme) => ({ root: { '& > *': { margin: theme.spacing(1), width: '25ch', textAlign: 'center' }, }, })); export default function TodoInput() { const classes = useStyles(); const [userInput, setUserInput] = useState({ id: '', task: '' }); const [todos, setTodos] = useState([]) //state for error const [error, setError] = useState({ errorMessage: '', error: false }) //add the user todo with the button const submitUserInput = (e) => { e.preventDefault(); //add the user input to array //task is undefined if (userInput.task === "") { //render visual warning for text input setError({ errorMessage: 'Cannot be blank', error: true }) console.log('null') } else { setTodos([...todos, userInput]) console.log(todos) setError({ errorMessage: '', error: false }) } console.log(loadedTodos) } //set the todo card to the user input const handleUserInput = function (e) { //make a new todo object setUserInput({ ...userInput, id: Math.random() * 100, task: e.target.value }) //setUserInput(e.target.value) //console.log(userInput) } const loadedTodos = []; for (const key in todos) { loadedTodos.push({ id: Math.random() * 100, taskName: todos[key].task }) } return ( <div> <Progress taskCount={loadedTodos.length} /> <form className={classes.root} noValidate autoComplete="off" onSubmit={submitUserInput}> {error.error ? <TextField id="outlined-error-helper-text" label="Today's task" variant="outlined" type="text" onChange={handleUserInput} error={error.error} helperText={error.errorMessage} /> : <TextField id="outlined-basic" label="Today's task" variant="outlined" type="text" onChange={handleUserInput} />} <Button variant="contained" color="primary" type="submit">Submit</Button> {userInput && <TodoCards taskValue={todos} />} </form> </div> ); }
TodoCards.js
import React, { useState } from 'react' import { Card, CardContent, Typography, FormControlLabel, Checkbox } from '@material-ui/core'; export const TodoCards = ({ taskValue }) => { const [checked, setChecked] = useState(false); //if checked, add the task value to the completed task array const completedTasks = []; const handleChecked = (e) => { setChecked(e.target.checked) //console.log('complete') for (const key in taskValue) { completedTasks.push({ id: Math.random() * 100, taskName: taskValue[key].task }) } } return ( < div > <Card> {taskValue.map((individual, i) => { return ( <CardContent key={i}> <Typography variant="body1"> <FormControlLabel control={ <Checkbox color="primary" checked={checked} onClick={handleChecked} /> } label={individual.task} /> </Typography> </CardContent> ) })} </Card> </div > ) }
Advertisement
Answer
This is because all of your checkboxes are connected to only one value (checked
). There is two way you could potentially solve this.
Method one:
Instead of a single value, you create a list that consists of as many values as you have checkboxes. Eg:
const [checked, setChecked] = useState([true, false, false]) //this is your list //... {taskValue.map((individual, index) => <Checkbox color="primary" checked={checked[index]} onClick={() => handleChecked(index)} /> }
In handleChecked
you should only change that one value based on the index.
Method number two (what I would probably do:
You create a new component for the check boxes
checktask.js
import {useState} from "react"; function CheckTask(props){ const [checked, setChecked] = useState(false); return ( <Checkbox color="primary" checked={checked[index]} onClick={() => handleChecked(index)} /> ) } export default CheckTask;
This way you could give each checkbox its own state.