The value of state is not reflecting in callback methods

Viewed 28

We have built a kind of a reusable menu bar which accepts a callback function with a string value of selectedMenuItem. The structure looks similar to the below -

Interface

export default interface MenuItem {
  title: string;
  id: string;
  onClick: (option: string) => void;
}

Initial Value -

[
    {
      title: "menu1",
      id: "MENU_1",
      onClick: (option) => {
        onMenuClick(option);
      }
    },
    {
      title: "menu2",
      id: "MENU_2",
      onClick: (option) => {
        onMenuClick(option);
      }
    }
  ]

The onMenuClick will probably have a switch to determine the next flow.

Example -

    const onMenuClick = (option) => {
switch (option) {
            case TopNavigationMenuItems.Security:
                navigate('../security');
                break;
            case TopNavigationMenuItems.Members:
                navigate('../members');
                break;
            case TopNavigationMenuItems.Budgets:
                navigate('../budgets');
                break;
            case TopNavigationMenuItems.Files:
                navigate('../files');
                break;
            default:
                break;
        }
      };
}

Now, I need to read a value from a state (declared with a useState hook).

The problem is that when the menu button is clicked, it only shows the initial state declared in useState. The new value of the state is not reflected in the callback functions.

I have tried to demonstrate the problem in the codesandbox - https://codesandbox.io/s/winter-glitter-fyk1ud

How can I ensure the callback function always refers to the updated state? Slightly confused here.

If you can recommend better ways of achieving this, that would be extremely helpful too.

2 Answers

The callback function receives the state at the time the callback is created. When state changes (it points to new address), your old callback still refers to the old state (old address). To use new state, you have to create new callback function every time new state is updated. looks something like below:


function App() {

  function handleClickMenu (option) {
    console.log("------------------------------------------------");
    console.log("selected option is - ", option);
    console.log("value of selectedFiles is", selectedFiles);
    console.log("------------------------------------------------");

  }

 return (
  /*ui...*/
     <NavigationComponent menu={menu} onFilesSelected={onFilesSelected} handler={handleClickMenu} />
   
 );

}


interface NavigationComponentProps {
  onFilesSelected: (files: string[]) => void;
  menu: MenuItem[];
  handler: any;
}

function NavigationComponent({
  onFilesSelected,
  menu = [],
  handler
}: NavigationComponentProps) {

 return (
  /* logic menu.map...*/
  <button
            onClick={() => {
              // item.onClick(item.title);
              handler(item.title);
            }}
          >
 );
}

Just initialise your menu with [] empty array and then in a use-effect, setmenu to set new states.

const [menu, setMenu] = useState<MenuItem[]>([]); // [] initially

useEffect(() => {
setMenu([{
  title: "menu1",
  id: "MENU_1",
  onClick: (option) => {
    onMenuClick(option);
  }
},
{
  title: "menu2",
  id: "MENU_2",
  onClick: (option) => {
    onMenuClick(option);
  }
}])
}, [onMenuClick]);

Sandbox link : https://codesandbox.io/s/nice-pateu-ocozo1?file=/src/App.tsx

Related