0

I have an array set in state like:

const Theme = {
  name: "theme",
  roots: {
    theme: Theme,
  },
  state: {
    theme: {
      quiz: {
        quizGender: null,
        quizSleepComfort: {
          justMe: {
            soft: null,
            medium: null,
            firm: null,
          },
          partner: {
            soft: null,
            medium: null,
            firm: null,
          }
        },
      },
    },
  },
  actions: {
    // ...
  },
};

I then have a component that has checkboxes, one for soft, medium, and firm. The code for the component is:

const Question = ({ state }) => {
  const [checkedItems, setCheckedItems] = useState([]);

  const checkboxes = [
    { 
      label: "Firm",
      value: "firm",
    },
    {
      label: "Medium",
      value: "medium",
    },
    {
      label: "Soft",
      value: "soft",
    },
  ];

  state.theme.quiz.quizSleepComfort.justMe = checkedItems;

  return (
    <QuestionCommonContainer>
      {checkboxes.map((item, id) => (
        <QuizCheckbox
          label={item.label}
          name={item.label}
          value={item.value}
          selected={checkedItems[item.value] === true}
          onChange={(e) => {
            setCheckedItems({
              ...checkedItems,
              [e.target.value]: e.target.checked,
            });
          }}
        />
      ))}
    </QuestionCommonContainer>
  );
};

export default connect(Question);

This specific component is just interacting with state.theme.quiz.quizSleepComfort.justMe object, not the partner object.

As of right now when a checkbox is selected, let's say the checkbox for "firm" is checked, the state gets updated to what looks like this:

...
quizSleepComfort: {
  justMe: {
    firm: true,
  },
  partner: {
    soft: null,
    medium: null,
    firm: null,
  }
},
...

I am trying to figure out how I would be able to alter this components code so that instead of setting the justMe object to include only the items that are checked (in this case "firm"), it should keep the other items as well ("soft", "medium") as null.

Please let me know if there is more info i should provide.

1
  • the error is here: state.theme.quiz.quizSleepComfort.justMe = checkedItems; you are explicitly changing the structure of the obj. Julian has a good answer Commented Nov 2, 2020 at 17:42

1 Answer 1

1

Okay. So the following is bad practice state.theme.quiz.quizSleepComfort.justMe = checkedItems;

You should pass a function to the Question component, something like onChange. The onChange function should update the state in your parent component. Use the spread operator ... to get a copy of the old object. for example

const onChange = (newState) =>
  setState((oldState) => ({
    ...oldState,
    justMe: { ...oldState.justMe, ...newState },
  })); 

the resulting object will contain all the properties of the original state but will overwrite any property set on newState in justMe. If the property that you want to update is more nested, just repeat the steps of spreading.

--- UPDATE ---

I have added an example that I think is close to what you are trying to achieve.

const Parent = () => {
  const [state, setState] = useState(initialState);

  const onChange = useCallback(
    (newState) =>
      setState((oldState) => ({
        ...oldState,
        theme: {
          ...oldState.theme,
          quiz: {
            ...oldState.theme.quiz,
            quizSleepComfort: {
              ...oldState.theme.quizSleepComfort,
              justMe: {
                ...oldState.theme.quizSleepComfort.justMe,
                ...newState,
              },,
            },
          },
        },
      })),
    [],
  );

  return <Question onChange={onChange} />;
};

const checkboxes = [
  {
    label: 'Firm',
    value: 'firm',
  },
  {
    label: 'Medium',
    value: 'medium',
  },
  {
    label: 'Soft',
    value: 'soft',
  },
];

const Question = ({ onChange }) => {
  const [checkedItems, setCheckedItems] = useState([]);

  useEffect(() => {
    onChange(checkedItems);
  }, [checkedItems, onChange]);

  return (
    <QuestionCommonContainer>
      {checkboxes.map((item, id) => (
        <QuizCheckbox
          label={item.label}
          name={item.label}
          value={item.value}
          selected={checkedItems[item.value] === true}
          onChange={(e) => {
            setCheckedItems((oldCheckedItems) => ({
              ...oldCheckedItems,
              [e.target.value]: e.target.checked,
            }));
          }}
        />
      ))}
    </QuestionCommonContainer>
  );
};

export default connect(Question);

As you are having a really nested object to update, it might be worth taking a look at Object.assign

Sign up to request clarification or add additional context in comments.

3 Comments

ahh thank you. I am a little bit confused though on what i am needing to replace "newState" and "oldState" with?
look at Object.assign() that will give you a good idea as to what's happening
I updated the answer with a closer example to what you are trying to achieve

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.