Managing State with Nested Objects in React

One of the most critical aspects of building applications in React is state management. It's a key concept that can sometimes be tricky, especially when dealing with nested objects and arrays. Today, I learned a neat trick to handle state changes in a nested object that might help you out.

The Challenge

Think about the following scenario: you are developing a page that has a number of sections that users can choose from a menu. The chosen menu item is saved as a state that includes a number of attributes. When the properties of the currently selected menu item change, you require the component to re-render, but React isn't detecting these changes because the state object is nested.

Why does this happen? React's state updates are shallow, meaning they don't deeply compare nested properties.

The Scenario

Imagine we have a Menu component, which renders different menu items like Home, About, Contact, etc. The user can select one of these items. Each menu item is an object with properties such as title, description, and isActive.

The selected menu item is stored in a state, and when properties of the selected item (such as description) change, we want the component to re-render. However, due to the nested nature of our state, React fails to recognize these changes and doesn't trigger a re-render.

The Solution

Instead of storing the whole selected object in state, we can store just the identifier (like the title) of the selected menu item. Then, we can use that identifier to find the corresponding menu item from the menu items array.

export const Menu = ({
  menuItems = [],
  labels = {},
}: MenuProps) => {
  const [selectedMenuItemTitle, setSelectedMenuItemTitle] = useState<string>(menuItems[0].title);
  
  const selectedMenuItem = menuItems.find((item) => item.title === selectedMenuItemTitle) || menuItems[0];

  useEffect(() => {
    setSelectedMenuItemTitle(menuItems[0].title);
  }, [menuItems]);

  const handleMenuItemClick = (menuItemTitle: string) => {
    setSelectedMenuItemTitle(menuItemTitle);
  };

  // rest of the component
};

In this code snippet, we've stored the title of the selected menu item in state. This title acts as a key to find the selected menu item from the menu items array. We also ensure that when the menuItems array changes, we reset the selected menu item title to the title of the first item in the updated array.

This approach lets us efficiently manage state changes for nested objects and ensures that React recognizes changes to nested properties, triggering a re-render when necessary.