skip to Main Content

I have a useState object of:

const [filterState,setFilterState] = useState({
        Brands:{
            NewBalance: false,
            Nike: false,
            Addiddas: false,
            Converse:false,
            UnderArmour:false,
        },
        Gender:{
            Male: false,
            Female: false,
            Kids:false,
        }
    });

and i am rendering each brand as a checkbox like this:

{
  Object.entries(filterState.Brands).map(([brand, checked])=>
     <div className="" key={brand} >
         <label htmlFor={brand} className="mr-[10px]">{brand}: </label>
         <input type="checkbox" name={brand} className="cursor-pointer" checked={checked} 
         onChange={()=>setFilterState({Brands:{...filterState.Brands,Nike:!checked},Gender: 
         {...filterState.Gender}})}/>
         </div>
)}

If you look at the onChange that’s where I have the problem:

onChange={()=>setFilterState({Brands{...filterState.Brands,Nike:!checked},Gender{...filterState.Gender}})}/> 

, if I give it an exact key like Nike then I have no issue but that only changes one checkbox. If I change the Nike to brand then I get an error of: 'brand' does not exist in type ... How do I make this work as brand is technically a string so it doesn’t work, how do I make it check/uncheck the correct boxes, thanks

3

Answers


  1. Your object is not really TS friendly, try changing to the following object style.

      const [filterState,setFilterState] = useState<Shoes>({
        brand:[{
            brand: "NewBalance",
            checked: false,
        },
        {
            brand: "Nike",
            checked: false,
        },
        ......
       ],
       gender: [{
    
       }]
    })
    

    Then you can declare your type as something like

    type Brand = {
      brand: string;
      checked: boolean;
    }
    
    type Gender = {
      gender: "Male" | "Female" | "Kid";
      checked: boolean;
    }
    
    type Shoes = {
      gender: Array<Gender>; 
      brand: Array<Brand>;
    }
    

    Then you no longer need to use Object.entries, you can use basic maps and filter

    codesandbox

    Login or Signup to reply.
  2. You have a syntax error in your onChange. Should be:

    onChange={()=>setFilterState({Brands:{...filterState.Brands,Nike:!checked},Gender:{...filterState.Gender}})}/>
    

    A bit more legibly, that’s:

    onChange={() => setFilterState({ 
      Brands: { ...filterState.Brands, Nike: !checked },
      Gender: {...filterState.Gender }
    })}/>
    
    Login or Signup to reply.
  3. The typing of Object.entries can be improved.

    // an alias that has a better typing than the original.
    const entries: <T extends object>(o: T) => ({ [K in keyof T]: [K, T[K]] }[keyof T])[] = Object.entries;
    
    const [filterState, setFilterState] = useState({
        Brands: {
            NewBalance: false,
            Nike: false,
            Addiddas: false,
            Converse: false,
            UnderArmour: false,
        },
        Gender: {
            Male: false,
            Female: false,
            Kids: false,
        }
    });
    
    entries(filterState.Brands).map(([brand, checked]) =>
        <div className="" key={brand} >
            <label htmlFor={brand} className="mr-[10px]">{brand}: </label>
            <input type="checkbox" name={brand} className="cursor-pointer" checked={checked}
                onChange={() => setFilterState(prev => ({ ...prev, Brands: { ...prev.Brands, [brand]: !checked } }))} />
        </div>
    )
    

    TS Playground

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search