Search code examples
arraysreactjsobjectcheckboxdynamicform

Implement dynamic checkbox with checked handler in React js


I'm having problems setting state for my dynamic checkbox for my array, changing an object from false to true, using a checked handler.

below is my code:

const [form, setForm] = useState({
    tags:[
     { name: "Athletic/Higher caloric", isChecked: false },
     { name: "Aggressive Weight Loss", isChecked: false },
     { name: "Quick and Easy", isChecked: false } ]})

const onCheckedHandler = (index) => {
      const updatedTags = 
        form.tags.map((tag, i) => {
           if (index === i) {
             form.tags[i].isChecked = !form.tags[i].isChecked;
             } 
           return { tag }; 
        }); 

    setForm(updatedTags);


return (
   {form.tags.map((tag, i) => (
        <div>
        <label>{tag.name}</label> 
        <input 
           type="checkbox"
           checked={tag.isChecked}
           onChange={(event) => onCheckedHandler(i)}
           key={i} /> 
        </div>
       ))})

I get this error: "Uncaught TypeError: Cannot read properties of undefined (reading 'map') at Create (line:107)"

But line 107 is just the start of another map function that works and is totally unrelated. What am I doing wrong? I tried avoiding mutating the isChecked value directly in the object with an if statement. Maybe it's how I setState?


Solution

  • It is my understanding that:

    • Initial state is an object const [form, setForm] = useState({...});
    • This has a prop named tags.
    • But, the variable updatedTags (in the handler-method) is an array. It's not an object.
    • So, setForm(updatedTags) will set form to be an array which will not have any prop named tags

    Consequently, please try changing the handler like so:

    const onCheckedHandler = (index) => {
      setForm(prev => ({
        ...prev,
        tags: [
          ...prev?.tags?.map(
            ({isChecked, ...rest}, idx) => (
              idx === index
              ? {...rest, isChecked: !isChecked}
              : {...rest, isChecked}
            )
          )
        ]
      }));
    };
    

    Also, please use checks before attempting map on the return as well. May be try something like so:

    form?.tags?.map(...)

    OR

    form && form.tags && Array.isArray(form.tags) && form.tags.map(...)