Search code examples
javascriptreactjsreact-reduxredux-toolkit

Updating state of different instances of the same component using React Redux


I have two instances of a component which I want them to behave like toggle buttons. If I click on a specific component instance, the other component instances should get deselected.

enter image description here

The below is my code:

store.js:

import { configureStore } from '@reduxjs/toolkit'
import boxReducer from './slices/boxSlice'

export default configureStore({
  reducer: {
    box:boxReducer
  }
})

boxSlice.js:

export const boxSlice = createSlice({
  name: "box",
  initialState: {
      selected: false,
  },
  reducers: {
    select: (state) => {
    state.selected=true;
    },
    deselect: (state) => {
        state.selected=false;
    },
  },
});

export const { select, deselect } = boxSlice.actions;
export default boxSlice.reducer;

Box.js:

export function Box(){
  const selectedStatus = useSelector((state) => state.box.selected)
  const dispatch = useDispatch()
  const [status, setStatus] = useState("Unselected")

  const onSelect = (e) => {
    dispatch(select());
    if (selectedStatus) {
      setStatus("Selected")
      dispatch(deselect());
    } else {
      setStatus("Unselected");
    }
  }

  return (
    <div tabIndex="0" onClick={onSelect} className="box">
      {status}
    </div>
  )
}

This is buggy. What I am observing is:

  • On first click the instance does not get updated. It gets updated only on second click
  • It does not toggle properly. Both remain selected or unselected

How do I update the other instances of the component properly, whenever one of the instances is clicked


Solution

  • The redux state should be the source of truth. I suggest giving each box component a unique id and store the currently selected box id. If the same box id is already selected, deselect the box id, otherwise, set to the new id.

    Example:

    boxSlice.js

    import { createSlice } from "@reduxjs/toolkit";
    
    export const boxSlice = createSlice({
      name: "box",
      initialState: {
        selected: null
      },
      reducers: {
        select: (state, { payload }) => {
          state.selected = state.selected === payload ? null : payload;
        }
      }
    });
    
    export const { select } = boxSlice.actions;
    export default boxSlice.reducer;
    

    Box

    function Box({ id }) {
      const dispatch = useDispatch();
    
      const selected = useSelector((state) => state.box.selected);
      const isSelected = selected === id;
    
      const onSelect = () => {
        dispatch(select(id));
      };
    
      return (
        <div tabIndex="0" onClick={onSelect} className="box">
          {isSelected ? "Selected" : "Unselected"}
        </div>
      );
    }
    

    App

    <Box id={1} />
    <Box id={2} />
    

    Edit updating-state-of-different-instances-of-the-same-component-using-react-redux