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.