自定义Hook仅在部分组件中返回陈旧值问题排查
问题
我编写了一个管理用户筛选器设置的自定义Hook:用户登录时通过Redux及相关API与后端交互,以Redux作为唯一数据源;未登录时则通过localStorage管理当前使用的筛选器。
遇到的异常情况:
- 在筛选器表单组件
Form中,无论登录状态如何,Hook返回的filters始终是最新且正确的值 - 在其他组件(如
SomewhereElse)中,未登录状态下filters始终停留在初始挂载时的值,不会随操作更新
代码示例:
const Form = () => { const { filters, setFilters } = useFilters(); // 此处filters始终正确且最新 }
const SomewhereElse = () => { const { filters } = useFilters(); // 未登录时,该值在初始挂载后从未改变 }
Hook代码:
import { useLocalStorage } from "usehooks-ts"; /** * Custom hook to help get and set filters, with different * effects based on whether or not the user is logged in. If logged in, * filters are read from and write to redux store and api. If not logged in, * filters are read from and write to localstorage */ export const useFilters = () => { const dispatch = useDispatch(); const authenticated = useSelector( (state: ApplicationState) => state.user.auth.authenticated ); const activeFiltersRedux = useSelector( (state: ApplicationState) => state.user.info?.settings?.filters ); const [localFilters, setLocalFilters] = useLocalStorage( "localFilters", JSON.stringify(defaultUserFilters) ); const activeFiltersLocal: { [key: string]: string[] } = JSON.parse(localFilters); const activeFilters = authenticated ? activeFiltersRedux ?? defaultUserFilters : activeFiltersLocal; const [filters, setFilters] = useState(activeFilters); console.log(filters); // 此处始终是最新值 // 用于同步本地状态与远程设置的Effect useEffect(() => { if (authenticated && !isEqual(filters, activeFilters)) { setFilters(activeFilters); } }, [activeFilters, authenticated]); // 当本地状态更新时,同步到API或localStorage useEffect(() => { if (authenticated) { dispatch( ActionCreators.updateSettings({ filters, }) ); } else { setLocalFilters(JSON.stringify(filters)); } }, [filters, authenticated]); return { filters, setFilters }; };
注:useLocalStorage来自usehooks-ts包,内部通过事件派发和state更新确保所有消费者同步获取最新值。直接在SomewhereElse中使用useLocalStorage("localFilters")能拿到最新值,但希望将Redux和localStorage的逻辑集中在Hook中统一使用。
原因分析
问题核心在于未登录状态下,localStorage更新后没有同步到Hook内部的filters本地状态:
- 未登录时,
activeFilters依赖activeFiltersLocal,而activeFiltersLocal由useLocalStorage返回的localFilters解析而来。当localFilters更新时,activeFilters会随之变化,但当前的同步Effect仅在authenticated为true时才更新filters,未覆盖未登录场景。 useState(activeFilters)仅在Hook首次挂载时初始化filters值,后续activeFilters变化不会自动更新filters,必须通过Effect手动同步。- 表单组件中
filters能更新是因为直接调用了setFilters,但其他组件的Hook实例中,activeFilters更新后没有触发同步逻辑,导致filters一直停留在初始值。
解决方案
修改同步状态的Effect,去掉仅在登录状态下同步的限制,让它在任何时候只要activeFilters和filters不一致,就更新filters:
useEffect(() => { // 无论登录状态,只要数据源的filters和本地状态不一致就同步 if (!isEqual(filters, activeFilters)) { setFilters(activeFilters); } }, [activeFilters, authenticated]);
补充说明
- 当用户通过
setFilters修改筛选器后,第二个Effect会同步更新Redux(登录时)或localStorage(未登录时),此时activeFilters会变成和filters一致的值,isEqual判断为true,不会触发重复更新,避免循环。 - 修改后,所有使用
useFilters的组件,无论是否登录,都会在数据源(Redux或localStorage)更新时同步获取最新的filters值。
内容的提问来源于stack exchange,提问作者Seth Lutske
相关产品推荐
相关产品推荐

