Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to have a default dynamic value derived from other atom [Recoil]

Tags:

recoiljs

I am developing an app, which has sidebar menu. I have an atom, which saves the state of the /menu and an atom which saves the last selected menu key (as this key is used for other selectors too) -> for getting specific info for the current selected key.

export const menuItems = atom({
  key: "menuItems",
  default: ({ get }) => get(baseApi)("/menu"),
}); -> Returns Menu Items

And then I have an atom, which saves the selected menu item key:

export const selectedMenuKey = atom<string>({
  key: "selectedMenuKey",
});

I cannot prefix the initial selected menu key as I don't know it in advance. I want the behavior to be following:

If the key is not set (when the app initially runs) set the selectedMenuKey value to be the first item of the menuItems atom value, otherwise be whatever is set last.

What would you say is the best way to achieve this?

like image 532
Mihail Yonchev Avatar asked Nov 27 '25 12:11

Mihail Yonchev


1 Answers

I ran into this exact use case. Here is what I ended up doing.

In my 'activeTabState' file, equivalent to your 'selectedMenuKey':

import { atom, selector } from 'recoil';

import formMapState from './formMapState';

const activeTabState = atom({
  key: 'activeTabAtom',
  default: selector({
    key: 'activeTabDefault',
    get: ({ get }) => {
      const formMap = get(formMapState);

      if (!formMap) return null;

      const [defaultTab] = Object.keys(formMap);

      return defaultTab;
    },
  }),
});

export default activeTabState;

Then you can update the tab just like any other recoil state:

const FormNavigationTab = (props) => {
  const { text, sectionName } = props;
  const [activeTab, setActiveTab] = useRecoilState(activeTabState);

  return (
    <NavigationTab active={activeTab === sectionName} onClick={() => setActiveTab(sectionName)}>
      {text}
    </NavigationTab>
  );
};

One thing to watch out for is that your activeTab value will be null until the menu items are loaded. So based on my use case, I needed to add a safeguard before using it:

const FormPage = () => {
  const map = useRecoilValue(formMapState);
  const activeTab = useRecoilValue(activeTabState);

  // Starts out null if the map hasn't been set yet, since we don't know what the name of the first tab is
  if (!activeTab) return null;

  const { text, fields, sections } = map[activeTab];
  
// ... the rest of the component
like image 74
jaek Avatar answered Nov 30 '25 06:11

jaek