You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义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本地状态:

  1. 未登录时,activeFilters依赖activeFiltersLocal,而activeFiltersLocal由useLocalStorage返回的localFilters解析而来。当localFilters更新时,activeFilters会随之变化,但当前的同步Effect仅在authenticated为true时才更新filters,未覆盖未登录场景。
  2. useState(activeFilters)仅在Hook首次挂载时初始化filters值,后续activeFilters变化不会自动更新filters,必须通过Effect手动同步。
  3. 表单组件中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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 21:41:11