Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React: useState filter array not updating state

Edit:

My error occured because I passed an array as a second parameter to useEffect. Even though the values inside the array stayed the same, the reference changed constantly, therefore useEffect was called constantly and reset my checkbox values. That array was created by an useState call. I replaced useState by useReducer (reducer only changes the object reference if the object is actually changed) and updated some missing dependencies higher up the component tree.

Original question:

I have trouble updating a state in a functional component.

My question is somewhat similiar to this one: React SetState doesn't call render

I'm already copying my state object (by using array.filter) instead of referencing it; but my state still doesn't update.

In order to track down the problem, I tried re-creating the problem in a minimal example:

jsfiddle

But in my minimal example, everything works as expected. I'm unable to reproduce the error.

Here is my example where the state doesn't update:

configCheckboxGroup.tsx:

import classNames from "classnames";
import React, { useState, useEffect } from "react";
import { Component } from "../../model";
import CheckboxPanel from "./panels/checkboxPanel";

interface configCheckboxGroupProps {
    className?: string;
    choices: Array<Component>;
    selected: Array<string>;
    addToCart: (items: Array<Component>) => void;
}

const ConfigCheckboxGroup: React.SFC<configCheckboxGroupProps> = ({
    className,
    choices,
    selected,
    addToCart,
}) => {

    const [ selectedComp, setSelectedComp ] = useState<Array<string>>(selected);

    // device loads later, selected has to be updated
    useEffect(() => {
        setSelectedComp(selected);
    }, [selected]);

    const handleOnChange = (ev: React.FormEvent, id: string) => {
        console.debug(id);
        console.debug(selectedComp.filter(el => el !== id));
        if (selectedComp.includes(id)) {
            // was already checked || this line is not working!
            setSelectedComp(selectedComp.filter(el => el !== id));
        } else {
            // was not checked
            setSelectedComp([...(selectedComp), id]);
        }

        const selected = choices.filter(el => selectedComp.includes(el.reference._id));
        addToCart(selected);        
    };

    return (
        <div className={classNames("panellist", className)}>
        {
            choices.map(el => {
                return (
                    <CheckboxPanel 
                        image={ el.reference.picture ? el.reference.picture : undefined }
                        name={ el.reference.name }
                        id={ el.reference._id }
                        price={ el.reference.price ? el.reference.price : 
                            el.price ? el.price : 0 } 
                        key={ el._id }
                        checked={ selectedComp.includes(el.reference._id) }  
                        onChange={ handleOnChange }
                    />
                )
            })
        }
        <span>
        { selectedComp }
            </span>
            </div>
    )
}

export default ConfigCheckboxGroup;

And checkboxPanel.tsx:

import classNames from "classnames";
import React from "react";

import "./checkboxPanel.scss";

import noImage from "../../../resources/images/errors/no-image.svg";

interface PanelProps {
    className?: string;
    image?: string;
    name: string;
    id: string;
    price: number;
    checked: boolean;
    onChange: (ev: React.FormEvent, id: string) => void;
}

const CheckboxPanel: React.SFC<PanelProps> = ({
    className,
    image,
    name,
    id,
    price,
    checked,
    onChange,
}) => {

    const getImage = () => {
        if (image) {
            return image;
        } else {
            return noImage;
        }
    }

    return (
        <div className={classNames("panel", "checkbox-panel", className)}>
            <div className="top">
                <div className="image">
                    <img alt="Product" src={getImage()} />
                </div>
                <div className="name">
                    {name}
                </div>
            </div>
            <div className="bottom">
                <div className="category">
                    { Number(price).toFixed(2) } €
                </div>
                <div>
                    <input type="checkbox" 
                        checked={ checked }
                        onChange={ (e) => onChange(e, id) }
                    />                    
                </div>
            </div>
        </div>
    )

};

export default CheckboxPanel;

The only difference between the examples is that in the second one, I call the handle function inside a child component. But I do the same thing on other occasions as well: I have a very similar Component configRadioGroup with radio buttons instead of checkboxes where everything works fine.

I tried playing around by manually filtering the array and trying a lot of other things, but nothing seemed to help. This is why, as a last try, I ask here (although I know that this question is not a good one due to it being very specific).

like image 340
schadenfreude Avatar asked Jul 02 '26 23:07

schadenfreude


1 Answers

Changing the prop selected will reset selectedComp if you put a console log in your useEffect you may find that that is resetting it every time.

You need to track down where selected comes from (redux?) and how it's set (addToCart?).

A dirty fix could be to only set selectedComp when component mounts, this is dirty and will/should cause react-hooks/exhaustive-deps lint to trigger:

useEffect(() => {
  setSelectedComp(selected);
}, []);

But better to track down what's going wrong with selected, if it comes from redux then maybe just use selected instead and forget about selectedComp since that is just a copy.

like image 161
HMR Avatar answered Jul 05 '26 15:07

HMR



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!