4

I have this component

const CheckboxColumn: FC<ICheckboxColumnProps> = ({ id_llamada }) => {
    // select  pickup

    const dispatch = useTypedDispatch();

    const [isPickupSelected, setIsPickupSelected] = useState(false);

    const selectPickup = (e: ChangeEvent<HTMLInputElement>) => {
        setIsPickupSelected(e.target.checked);

        dispatch(controlPickup(id_llamada));
    };

    return (
        <TableCell
            component={'div'}
            padding="checkbox"
            sx={{ padding: '0px 0px 0px 4px' }}
            onClick={(e) => {
                e.stopPropagation();
            }}
        >
            <Checkbox
                color="primary"
                checked={isPickupSelected ? true : false}
                // disabled={true}
                sx={{
                    padding: '7px',
                    '&:hover': {
                        backgroundColor: 'rgba(0, 0, 0, 0.06)',
                    },
                }}
                onChange={selectPickup}
            />
        </TableCell>
    );
};

export default CheckboxColumn;

And I need to be able to select a table's row, but also dispatch its information to redux, the problem, is that, I can't figure out how to use setState along with dispatch.

What is currently happening is that my isPickupSelected is not updating its value, but data it's actually being saved in my reducer, and, when I comment the dispatch(function()) my state is being able to work properly.

I've been trying to search solutions to this, and one of them was that, I should use useEffect and whenever my state changes, I should dispatch the function, and it works, but immediately, it literally restarts my component, and also my isPickupSelected so my state is no longer updated but returns to its original value of false.

What am I supposed to do in this case?

SuperStormer
  • 4,997
  • 5
  • 25
  • 35
Diego
  • 421
  • 3
  • 9
  • 34

4 Answers4

3

You shouldn't have to make the equality check below since isPickupSelected is already a boolean value.

checked={isPickupSelected ? true : false}

Try simply using:

<Checkbox
  color="primary"
  checked={isPickupSelected}
  ...

Also, given that the checkbox can only be checked or unchecked, you actually could do:

const selectPickup = () => {
  setIsPickupSelected(!isPickupSelected) // instead of `e.target.checked`

This way, when the user clicks on the checkbox, the state setter will set isPickupValue to the opposite of whatever the current value is (toggling).

Let me know if that fixes your issues? Cheers

Moa
  • 1,456
  • 4
  • 20
2

The difference between how your dispatch is updating the state vs your setIsPickupSelected, is your dispatched state is relying on the previous state value to determine the next, where as your setter is relying on the UI elements value. That UI elements value is being controlled by the isPickupState, and so when the checkbox is clicked, the value passed in will be the value you get from e.target.checked. To fix this, you will need to control that state separately from the UI element. You are already doing this with the isPickupSelected state variable.

Also, since your checkbox component is the only one consuming a state, you may be able to save your TableCell a rerender each time that checkbox changes. All you have to do is hoist that logic into a separate component.

const ControlledCheckbox : FC<IControlledCheckboxProps> = () => {

    const dispatch = useTypedDispatch();

    const [isPickupSelected, setIsPickupSelected] = useState(false);

    const selectPickup = () => {
        setIsPickupSelected(!isPickupSelected);

        dispatch(controlPickup(id_llamada));
    };
    return <Checkbox
                color="primary"
                checked={isPickupSelected ? true : false}
                // disabled={true}
                sx={{
                    padding: '7px',
                    '&:hover': {
                        backgroundColor: 'rgba(0, 0, 0, 0.06)',
                    },
                }}
                onChange={selectPickup}
            />
}

const CheckboxColumn: FC<ICheckboxColumnProps> = ({ id_llamada }) => {
    return (
        <TableCell
            component={'div'}
            padding="checkbox"
            sx={{ padding: '0px 0px 0px 4px' }}
            onClick={(e) => {
                e.stopPropagation();
            }}
        >
           <ControlledCheckbox />
        </TableCell>
    );
};

export default CheckboxColumn;
san_anto
  • 120
  • 5
1

Solution 1: Remove checked prop from Checkbox, leave it uncontrolled input.

// checked={isPickupSelected ? true : false}

Solution 2: In handler func:

setIsPickupSelected(prevState => !prevState)
rei koleci
  • 31
  • 3
0

Is prop id_llamada coming from the Redux store?

If that's the case, then the component will re-render thus setting isPickedUpSelected back to false. Why? I assume the dispatch updates the value of id_llamada on a parent level causing the rendering process to catch the update and re-render the component as it gets passed back down to the component.

The above would be the root cause of why the component state is not updated and that's why when you comment out the dispatch the state update works.

To avoid this, you may want to use the store value for id_llamada instead of passing it as a prop. Perhaps you want to read about Redux Selectors.

Also, as an observation & unrelated to the initial problem, since isPickupSelected is a boolean you can simplify checked={isPickupSelected ? true : false} to checked={isPickupSelected}.

Ángel
  • 99
  • 1
  • 4