javascript 状态更新时,Zustand选择器功能未激发

wyyhbhjk  于 2023-02-02  发布在  Java
关注(0)|答案(1)|浏览(108)

所以我有两个Zustand存储,一个存储配置信息,比如当前在模态中查看的是哪个用户:

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

..,另一个存储其它应用数据:

export const usePermissionsStore = create<PermissionsStoreState>((set) => ({
    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);

    // This selector isn't firing when `useConfigStore.selectedUser` updates
    const permissionsForSelectedUser = usePermissionsStore((state) => state.getPermissionsForCurrentUser());

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

    React.useEffect(() => {
        // This useEffect triggers only once
        console.log("permissionsForSelectedUser updated...")
    }, [permissionsForSelectedUser]);

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

所以getPermissionsForCurrentUser()显然依赖于另一个存储中的selectedUser,但是当它改变时它不更新,我看不到一种方法来指定它应该更新。
问题是他们被分成了两个不同的商店吗?我该怎么解决这个问题?

5us2dqdw

5us2dqdw1#

是否有一个很强的理由需要有两个存储?如果没有,通常当我使用Zustand时,我通常会使用切片在一个存储中分解我的状态。
以下链接演示了如何进行设置:

可以有一个小的设置,以获得正是你想要的。
但如果我试图将您的逻辑重写到一个存储中,它可能看起来像这样:

export interface SliceContext<T extends object> {
  set: SetState<T>;
  get: GetState<T>;
  api: StoreApi<T>;
  getContext: () => Context;
}

/** Typings aren't the best here **/
export type SliceCreator<T extends object, U extends object> = (sliceContext: SliceContext<T>) => U;

export interface Context {
 /** 
 * This can be shared services between different mutations 
 * HTTP client, logging, etc.
 */
}

export interface ConfigSlice {
  user?: User;
  setSelectedUser: (user: User) => void;
}

export interface PermissionsSlice {
  userPermissions: UserPermission[];
  createDefaultPermissions: (users: Users[])  => void;
  getPermissionsForCurrentUser: () => UserPermission | undefined;
}

export interface StoreState {
  config: ConfigSlice;
  permissions: PermissionsSlice;
}

export const createConfigSlice: SliceCreator<StoreState, ConfigSlice> = ({ set }: Context)  => ({
    selectedUser: undefined,
    setSelectedUser: (user: User) => {
        set(
            // fyi: you could also create a immer middleware
            produce((draft) => {
                draft.selectedUser = user;
            })
        );
    }
});

export const createPermissionsSlice: SliceCreator<StoreState, PermissionSlice> = ({ set, get }: Context)  => ({
    userPermissions: [],
    createDefaultPermissions: (users: Array<Users>) => {
        set(
            produce((draft) => {
                draft.userPermissions = users.map(x => new UserPermission(x))
            });
        )
    },
    getPermissionsForCurrentUser: () => {
        const currentState = get();
        const userPermissions = currentState.permissions.userPermissions;
        const selectedUser = currentState.config.selectedUser;
        return userPermissions.find(x => x.user.id === selectedUser.id);
    }
});

const createStore: StoreApi<StoreState> | StateCreator<StoreState, SetState<StoreState>> = (set, get, api) => {
   const sliceContext: SliceContext = {
     set,
     get,
     api,
     getContext: () => {
       // Totally optional, but you could instantiate all your 
       // shared services.
     }
   }

   return {
     config: createConfigSlice(sliceContext),
     permissions: createPermissionsSlice(sliceContext)
   }
}

// You can apply all the other middleware here... 
export const useStore = create(createStore);

然后在Page.tsx中使用它

const MyPage = () => {
    const { users, isLoading } = useFetchUsers();
    const { permissionsForSelectedUser, setSelectedUser } = useStore(state => ({
        permissionsForSelectedUser: state.permissions.getPermissionsForCurrentUser(),
        setSelectedUser: state.config.setSelectedUser
    }), shallow);

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

    React.useEffect(() => {
        // This useEffect triggers only once
        console.log("permissionsForSelectedUser updated...")
    }, [permissionsForSelectedUser]);

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

相关问题