Skip to content
Advertisement

unable to only select a single material ui checkbox

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, setChacked] = 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.

Advertisement