Recoil:如何让selectedMenuKey默认取menuItems首个项并保留后续设置?
解决方案
你可以通过Recoil的selector实现这个需求,利用selector依赖其他原子的特性,自动处理默认值逻辑,同时保留后续手动设置的状态。
步骤1:调整原子定义(可选但更清晰)
明确selectedMenuKey的默认值为undefined,便于判断是否已设置:
export const menuItems = atom({ key: "menuItems", default: ({ get }) => get(baseApi)("/menu"), }); export const selectedMenuKey = atom<string | undefined>({ key: "selectedMenuKey", default: undefined, });
步骤2:创建带默认值逻辑的Selector
定义selectedMenuKeyWithDefault selector,它会:
- 读取时:如果
selectedMenuKey已有值则直接返回,否则取menuItems的第一个项的键 - 设置时:直接更新
selectedMenuKey的状态,保留用户手动设置的值
import { selector } from 'recoil'; export const selectedMenuKeyWithDefault = selector<string | undefined>({ key: "selectedMenuKeyWithDefault", get: ({ get }) => { const currentSelectedKey = get(selectedMenuKey); const menus = get(menuItems); // 优先使用已设置的选中键,否则取第一个菜单的键(如果菜单存在) if (currentSelectedKey !== undefined) { return currentSelectedKey; } return menus.length > 0 ? menus[0].key : undefined; }, set: ({ set }, newKey) => { set(selectedMenuKey, newKey); }, });
步骤3:在组件中使用Selector
在组件里通过useRecoilState使用这个selector,即可实现需求:
import { useRecoilState, useRecoilValueLoadable } from 'recoil'; import { selectedMenuKeyWithDefault, menuItems } from './your-recoil-state'; function Sidebar() { const [selectedKey, setSelectedKey] = useRecoilState(selectedMenuKeyWithDefault); const menuItemsLoadable = useRecoilValueLoadable(menuItems); // 处理菜单异步加载的状态 switch (menuItemsLoadable.state) { case 'hasValue': return ( <div className="sidebar"> {menuItemsLoadable.contents.map(item => ( <div key={item.key} className={selectedKey === item.key ? 'active' : ''} onClick={() => setSelectedKey(item.key)} > {item.label} </div> ))} </div> ); case 'loading': return <div>加载菜单中...</div>; case 'hasError': return <div>菜单加载失败</div>; } }
逻辑说明
- 应用首次运行时,
selectedMenuKey为undefined,selector会自动取menuItems的第一个项的键作为默认选中值 - 当用户点击菜单设置新的选中键后,
selectedMenuKey会被更新,后续selector读取时会优先使用这个手动设置的值,不会再回到默认的第一个项 - 用
useRecoilValueLoadable处理menuItems的异步加载状态,避免菜单未加载完成时出现异常
内容的提问来源于stack exchange,提问作者Mihail Yonchev
相关产品推荐
相关产品推荐

