1

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?

3
  • Please trim your code to make it easier to find your problem. Follow these guidelines to create a minimal reproducible example. Commented Apr 3, 2022 at 14:21
  • My bad, first time posting. Is that better? Commented Apr 3, 2022 at 14:46
  • Initial state is an object const [form, setForm] = useState({...});. This has a prop named tags. But, the variable updatedTags is just 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. Commented Apr 3, 2022 at 14:48

2 Answers 2

1

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(...)

Sign up to request clarification or add additional context in comments.

2 Comments

That actually worked! Ty!! The only thing is that it created a new index of 0 within the tag prop, and within the 0 index was the actual array with the correct boolean change of true. Is there a reason for that? So tag: 0 : [0: {..}, 1: {..}, 2: {..}]. Also, can you explain the logic you used for the answer? Much appreciated.
The answer has been updated. Within the setForm, for the tags prop, the array was enclosed within [ & ] so, it created nested array, by mistake. It is now fixed by using [...prev?.tags?.map(...)]. The ... spread operator should extract the result of the .map() and avoid the nesting. I think one may even choose to drop both [ - ] and the ... - but I prefer taking a copy (even if its only a shallow-copy).
0

The problem comes probably from here:

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

The issue is you're trying to render using the form state which at the time of mounting (of the component) is probably undefined. As your error says cannot read property of undefined, reading map it probably refers to that form.tags is undefined and can't read map of it.

A good idea is to check before rendering if what you're trying to render is defined or not. For example:

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

1 Comment

Ty for your help. I have two other .maps from form and they work. Although they are not checkboxes and they are not nested arrays like "tag" is, they are just objects with empty arrays. The state changes as its supposed to for their onChange handlers. That is a bit confusing. I also tried the ternary check method but got the same error message. If not reaching into form state to iterate, how do I iterate through the nested array?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.