1

I'm making an ecommrece project, in search bar I'm displaying checkboxes with categories from the API, all of those have value active == true by default. This is how I'm mapping them:

{categories.map((category) => {
       return (
              <>
             <input type="checkbox" onClick={() => handleCheckbox(category)} 
             defaultChecked={category.active} key={category.name} />
             <label style={{marginLeft:"5px"}} htmlFor={category.name}>{category.name}</label><br/>
             </> 
)
})}

Then I run this function to change category.active property from true to false, when the exact checkbox is clicked, then I want to update this object using useState

const handleCheckbox = (category) => {
    let tempCategory = category
    if (tempCategory.active == true) {
        tempCategory.active = false;
    } else {
        tempCategory.active = true;
    }
    setCategories({...categories, [tempCategory.id]: tempCategory})
    console.log(category.active)
}

Unfortunetly when I click on the checkbox I'm getting an error from React:

TypeError: categories.map is not a function

And it points to categories.map, I have no idea how to fix it. All I want is to update specific object in the categories array.

4
  • We seem to be missing the bit where categories is created. Commented Nov 29, 2021 at 10:55
  • 1
    When you click handleCheckbox you set categories to an object, so either categories are not an array and you should try doing Object.values(categories).map(), or change the setCategories to set it to an actual array Commented Nov 29, 2021 at 10:55
  • @KrzysztofKrzeszewski I changed setCategories to setCategories([...categories, tempCategory]) and it works, but now everytime I click on the checkbox in appends another object instead of updating an existing one Commented Nov 29, 2021 at 10:59
  • then maybe make it into an object instead, and leave the setter as is Commented Nov 29, 2021 at 11:04

1 Answer 1

1

I believe your categories start life as an array of category objects, and you meant to setCategories to an array - but instead you're setting it to an object.

You also need to make sure you dont double up tempCategory. something like this:

setCategories([
 ...categories.filter(x => x.id != tempCategory.id), 
 tempCategory
]);

However, this will reorder your categories, putting tempCategory at the end, so another option is to slice the array correctly

const tmpIdx = categories.findIndex(x => x.id == tempCategory.id);
setCategories([
     ...categories.slice(0,tmpIdx), 
     tempCategory,
     ...categories.slice(tmpIdx+1), 
]);

Another option is make your categories an object and use Object.values(categories).map(...) but this makes ordering your categories harder, as there is no fixed ordering of object keys/values like there is with an array.

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

5 Comments

It works, by when I click on the ckeckbox it changes it's place to the last one in form, FE: (X) Category 1 (X) Category 2 (X) Category 3 And I when uncheck Category 2 it goes like: (X) Category 1 (X) Category 3 (O) Category 2 I hope you understand. Do you know how to fix it?
@mateuszlaczynski you might be missing the update to this answer made before that comment. refresh the page
@mateuszlaczynski subtle bug - I used splice and i meant to use slice! Now updated
It works perfectly fine. Thanks you made my day! :)
Glad I could help, sorry for the confusing original answer :)

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.