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

Zustand选择器函数状态更新时未触发,跨store依赖问题如何解决?

问题描述

我有两个Zustand store:一个存储配置信息(如模态框中当前查看的用户):

export const useConfigStore = create<ConfigStoreState>((set) => ({
    selectedUser: undefined,
    setSelectedUser: (user: User) => {
        set(
            produce((draft) => {
                draft.selectedUser = user;
            })
        );
    }
}));

另一个存储其他应用数据:

export const usePermissionsStore = create<PermissionsStoreState>((set, get) => ({
    userPermissions: [],
    createDefaultPermissions: (users: Array<Users>) => {
        set(
            produce((draft) => {
                draft.userPermissions = users.map(x => new UserPermission(x))
            })
        );
    },
    getPermissionsForCurrentUser: () => {
        const selectedUser = useConfigStore.getState().selectedUser;
        return get().userPermissions.find(x => x.user.id === selectedUser.id);
    }
}));

在React组件中,我调用API获取用户并填充userPermissions,同时自动选中列表中的第一个用户。

问题在于,当userPermissions更新时,getPermissionsForCurrentUser()函数不会重新触发,也不会导致组件重新渲染:

const MyPage = () => {
    const { users, isLoading } = useFetchUsers();
    const setSelectedUser = useConfigStore((state) => state.setSelectedUser);

    // 当`useConfigStore.selectedUser`更新时,此选择器不会触发
    const permissionsForSelectedUser = usePermissionsStore((state) => state.getPermissionsForCurrentUser());

    React.useEffect(() => {
        if(users.length > 0) {
            setSelectedUser(users[0]); 
        }
    }, [users]);

    React.useEffect(() => {
        // 此useEffect仅触发一次
        console.log("permissionsForSelectedUser updated...")
    }, [permissionsForSelectedUser]);

    return (
        <>
            {isLoading || !users || !permissionsForSelectedUser ? (
                <div>Loading...</div>
            ) : (
                <div>Permissions are available!: {permissionsForSelectedUser.xyz}</div>
            )}
        </>
    );
};

显然getPermissionsForCurrentUser()依赖另一个store中的selectedUser,但当该状态变化时它不会更新,我也无法指定它应该更新。

请问问题是否源于拆分到两个不同的store?该如何解决此问题?

问题分析与解决方案

问题根源

拆分store本身不是问题,核心问题在于:

  • getPermissionsForCurrentUser内部用useConfigStore.getState()获取状态,这种方式不会让Zustand追踪到selectedUser的变化。组件订阅的是usePermissionsStore的状态,但selectedUser属于另一个store,Zustand无法自动关联两者的依赖。
  • 当selectedUser变化时,usePermissionsStore本身的状态没有改变,所以组件不会收到更新通知。

解决方案

这里提供几种可行的解决方式:

1. 将依赖的外部状态作为参数传入

修改getPermissionsForCurrentUser,让它接收selectedUser作为参数,在组件中同时获取两个store的状态并组合:

修改PermissionsStore:

export const usePermissionsStore = create<PermissionsStoreState>((set, get) => ({
    userPermissions: [],
    createDefaultPermissions: (users: Array<Users>) => {
        set(
            produce((draft) => {
                draft.userPermissions = users.map(x => new UserPermission(x))
            })
        );
    },
    // 改为接收参数,不再直接依赖外部store
    getPermissionsForUser: (selectedUser: User | undefined) => {
        if (!selectedUser) return undefined;
        return get().userPermissions.find(x => x.user.id === selectedUser.id);
    }
}));

组件中使用:

const MyPage = () => {
    const { users, isLoading } = useFetchUsers();
    const setSelectedUser = useConfigStore((state) => state.setSelectedUser);
    const selectedUser = useConfigStore((state) => state.selectedUser);
    const getPermissionsForUser = usePermissionsStore((state) => state.getPermissionsForUser);
    const userPermissions = usePermissionsStore((state) => state.userPermissions);

    // 组合两个状态,当任意一个变化时重新计算
    const permissionsForSelectedUser = React.useMemo(() => {
        return getPermissionsForUser(selectedUser);
    }, [selectedUser, userPermissions, getPermissionsForUser]);

    React.useEffect(() => {
        if(users.length > 0) {
            setSelectedUser(users[0]); 
        }
    }, [users]);

    React.useEffect(() => {
        console.log("permissionsForSelectedUser updated...")
    }, [permissionsForSelectedUser]);

    return (
        <>
            {isLoading || !users || !permissionsForSelectedUser ? (
                <div>Loading...</div>
            ) : (
                <div>Permissions are available!: {permissionsForSelectedUser.xyz}</div>
            )}
        </>
    );
};

2. 在store中订阅外部store的变化

如果希望逻辑保留在store内部,可以在usePermissionsStore中订阅useConfigStore的selectedUser变化,当它改变时主动触发store的更新:

修改PermissionsStore:

export const usePermissionsStore = create<PermissionsStoreState>((set, get) => ({
    userPermissions: [],
    createDefaultPermissions: (users: Array<Users>) => {
        set(
            produce((draft) => {
                draft.userPermissions = users.map(x => new UserPermission(x))
            })
        );
    },
    getPermissionsForCurrentUser: () => {
        const selectedUser = useConfigStore.getState().selectedUser;
        if (!selectedUser) return undefined;
        return get().userPermissions.find(x => x.user.id === selectedUser.id);
    }
}));

// 订阅config store的变化,当selectedUser改变时,触发permissions store的更新
useConfigStore.subscribe(
    (state) => state.selectedUser,
    () => {
        // 空set触发订阅者更新
        usePermissionsStore.setState({}, false);
    }
);

这样当selectedUser变化时,usePermissionsStore的订阅者(包括组件)会重新计算选择器,getPermissionsForCurrentUser也会重新执行。

3. 使用组合选择器跨store追踪依赖

利用createSelector(需安装@reduxjs/toolkit或reselect)创建跨两个store的选择器,自动追踪依赖:

import { createSelector } from '@reduxjs/toolkit';

// 定义基础选择器
const selectSelectedUser = (state: ReturnType<typeof useConfigStore.getState>) => state.selectedUser;
const selectUserPermissions = (state: ReturnType<typeof usePermissionsStore.getState>) => state.userPermissions;

// 组合选择器
const selectPermissionsForSelectedUser = createSelector(
    [selectSelectedUser, selectUserPermissions],
    (selectedUser, userPermissions) => {
        if (!selectedUser) return undefined;
        return userPermissions.find(x => x.user.id === selectedUser.id);
    }
);

// 组件中使用
const MyPage = () => {
    const { users, isLoading } = useFetchUsers();
    const setSelectedUser = useConfigStore((state) => state.setSelectedUser);
    
    // 使用组合选择器,自动追踪两个store的依赖
    const permissionsForSelectedUser = useConfigStore(
        usePermissionsStore.withStore(selectPermissionsForSelectedUser)
    );

    React.useEffect(() => {
        if(users.length > 0) {
            setSelectedUser(users[0]); 
        }
    }, [users]);

    React.useEffect(() => {
        console.log("permissionsForSelectedUser updated...")
    }, [permissionsForSelectedUser]);

    return (
        <>
            {isLoading || !users || !permissionsForSelectedUser ? (
                <div>Loading...</div>
            ) : (
                <div>Permissions are available!: {permissionsForSelectedUser.xyz}</div>
            )}
        </>
    );
};

内容的提问来源于stack exchange,提问作者MSOACC

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 08:10:24