I am new to react and I am making a simple todo app using react js and material ui. I have separate components to take in user input (TodoInput.js) which sends props to a component that renders individual todo tasks and displays a checkbox (TodoCards.js). What I want to do is display the total number of completed tasks onto the page which is updated when the user completes a todo by checking a checkbox. To achieve this, I have an array that stores all the user's completed tasks. At the moment whenever a checkbox is checked, all tasks are added to this array. I ran into a problem where I am unsure of how to only push values into this new array when the checkbox of that specific task is checked. Any guidance or explanations towards the right direction is 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';
import { CompletedTasks } from './CompletedTasks';
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)
for (const key in taskValue) {
completedTasks.push(taskValue[key])
}
console.log(completedTasks.length)
}
return (
< div >
<CompletedTasks completed={completedTasks.length} />
<Card>
{taskValue.map((individual, i) => {
return (
<CardContent key={i}>
<Typography variant="body1">
<FormControlLabel
control={
<Checkbox
color="primary"
checked={checked[i]}
onClick={handleChecked}
/>
}
label={individual.task} />
</Typography>
</CardContent>
)
})}
</Card>
</div >
)
}
CompletedTasks.js (displays the total number of completed tasks)
import React from 'react'
import InsertEmoticonOutlinedIcon from '@material-ui/icons/InsertEmoticonOutlined';
import { Typography } from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
},
paper: {
padding: theme.spacing(2),
marginTop: '20px',
textAlign: 'center',
color: theme.palette.text.secondary,
},
}));
export const CompletedTasks = ({ completed }) => {
const classes = useStyles();
return (
<div className={classes.root}>
<InsertEmoticonOutlinedIcon fontSize="large" />
<Typography variant="h6">
Completed tasks:{completed}
</Typography>
</div>
)
}