Search code examples
javascriptreactjsredux

React redux, each child component is getting the same value from redux


I have a parent component that maps out a list of child component. Each child component has their own props like name, id, etc. I want to persist the checkbox is checked value on page refresh so I am using redux for storage. When I check/uncheck an individual checkbox, all the others checkboxes also gets checked/unchecked.

component file

const Daily = ({ daily, character_id }) => {
  const { dailyName, id } = daily;
  const checkbox = useSelector(checkboxSelector)
  const dispatch = useDispatch()

  const deleteDaily = async (id) => {
    const dailyDoc = doc(db, `charactersDailies/${character_id}/dailies`, id)
    await deleteDoc(dailyDoc)
    window.location.reload(false)
  }

  return (
    <div className="dailyContainer">
      <input
        className="dailyCheckbox"
        id={daily.id}
        type="checkbox"
        checked={checkbox}
        onChange={() => dispatch(setCheckboxAction(daily.id))}
      />            
      <label className="dailyName" htmlFor={daily.id}>
        <h4 className="dailyName">{dailyName.toUpperCase()}</h4>
      </label>
      <h4 className="deleteDailyBTN" onClick={() => deleteDaily(id)}>
        &#10005;
      </h4>
    </div>
  )
};

export default Daily;

redux action file

export const setCheckboxAction = (id) => {
  return {
    type: 'SET-CHECKBOX',
    payload: id
  }
};

redux reducer file

const checkboxReducer = (state = null, action) => {
  switch(action.type) {
    case 'SET-CHECKBOX':
      return !state

    default:
      return state
  }
};

export default checkboxReducer;

How do I only access each individual checkbox state from redux instead of all of the checkboxes?


Solution

  • Issue

    All the checkboxes are using the same single checked state since it's a boolean.

    export const setCheckboxAction = (id) => {
      return {
        type: 'SET-CHECKBOX',
        payload: id
      }
    };
    
    const checkboxReducer = (state = null, action) => {
      switch(action.type) {
        case 'SET-CHECKBOX':
          return !state // <-- single boolean state
        default:
          return state
      }
    };
    
    const Daily = ({ daily, character_id }) => {
      ...
    
      const checkbox = useSelector(checkboxSelector); // <-- single state
    
      ...
    
      return (
        <div className="dailyContainer">
          <input
            ...
            checked={checkbox} // <-- single state
            ...
          />            
          ...
        </div>
      )
    };
    

    Solution

    Use the passed id payload of the setCheckboxAction action to conditionally toggle a specific checkbox by id. Convert the checkbox state to an object/map that uses the passed id as a computed property and toggles the truthy/falsey value. The UI will check the value using daily.id.

    Example:

    const checkboxReducer = (state = {}, action) => {
      switch(action.type) {
        case 'SET-CHECKBOX':
          return {
            ...state,
            [action.payload]: !state[action.payload], // payload -> id
          };
    
        default:
          return state;
      }
    };
    
    const Daily = ({ daily, character_id }) => {
      const { dailyName, id } = daily;
      const checkboxes = useSelector(checkboxSelector);
      const dispatch = useDispatch();
    
      const deleteDaily = async (id) => {
        const dailyDoc = doc(db, `charactersDailies/${character_id}/dailies`, id)
        await deleteDoc(dailyDoc)
        window.location.reload(false)
      }
    
      return (
        <div className="dailyContainer">
          <input
            className="dailyCheckbox"
            id={daily.id}
            type="checkbox"
            checked={checkboxes[daily.id]}
            onChange={() => dispatch(setCheckboxAction(daily.id))}
          />            
          <label className="dailyName" htmlFor={daily.id}>
            <h4 className="dailyName">{dailyName.toUpperCase()}</h4>
          </label>
          <h4 className="deleteDailyBTN" onClick={() => deleteDaily(id)}>
            &#10005;
          </h4>
        </div>
      );
    };