gpt4 book ai didi

reactjs - 如何在 Redux Toolkit 中订阅 React 组件之外的状态?

转载 作者:行者123 更新时间:2023-12-04 14:51:15 27 4
gpt4 key购买 nike

我有以下切片:

export const authenticationSlice = createSlice({
name: 'authentication',
initialState: {
isFirstTimeLoading: true,
signedInUser: null
},
reducers: {
signOut: (state) => {
state.signedInUser = null
},
setUserAfterSignIn: (state, action: PayloadAction<SignInResult>) => {
// some logic...
state.signedInUser = {...}
}
},
extraReducers: builder => {
// Can I subscribe to signedInUser changes here?
}
})
有什么方法可以订阅的时候 signedInUser更改( setUserAfterSignInsignOut ),内部 extraReducers ?
例如每次 setUserAfterSignIn我想在 axios 中添加一个拦截器,它使用用户的 accessToken 作为 Auth header 。
我也可以从另一个切片订​​阅这个状态吗?如果不同切片中的某个状态取决于 signedInUser ?
编辑:这是登录用户的 thunk 和注销的
export const { signOut: signOutAction, setUserAfterSignIn: setUserAction } = authenticationSlice.actions

export const signInWithGoogleAccountThunk = createAsyncThunk('sign-in-with-google-account', async (staySignedIn: boolean, thunkAPI) => {
const state = thunkAPI.getState() as RootState
state.auth.signedInUser && await thunkAPI.dispatch(signOutThunk())
const googleAuthUser = await googleClient.signIn()
const signedInUser = await signInWithGoogleAccountServer({ idToken: googleAuthUser.getAuthResponse().id_token, staySignedIn })
thunkAPI.dispatch(setUserAction({ data: signedInUser.data, additionalData: { imageUrl: googleAuthUser.getBasicProfile().getImageUrl() } } as SignInResult))
})

export const signInWithLocalAccountThunk = createAsyncThunk('sign-in-with-local-account', async (dto: LocalSignInDto, thunkAPI) => {
const state = thunkAPI.getState() as RootState
state.auth.signedInUser && await thunkAPI.dispatch(signOutThunk())
const user = await signInWithLocalAccountServer(dto)
thunkAPI.dispatch(setUserAction({ data: user.data } as SignInResult))
})

export const signOutThunk = createAsyncThunk<void, void, { dispatch: AppDispatch }>('sign-out', async (_, thunkAPI) => {
localStorage.removeItem(POST_SESSION_DATA_KEY)
sessionStorage.removeItem(POST_SESSION_DATA_KEY)

const state = thunkAPI.getState() as RootState
const signedInUser = state.auth.signedInUser

if (signedInUser?.method === AccountSignInMethod.Google)
await googleClient.signOut()
if (signedInUser)
await Promise.race([signOutServer(), rejectAfter(10_000)])
.catch(error => console.error('Signing out of server was not successful', error))
.finally(() => thunkAPI.dispatch(signOutAction()))
})

最佳答案

Redux 实现了 flux architecture .
Flux architecutre

This structure allows us to reason easily about our application in a way that is reminiscent of functional reactive programming, or more specifically data-flow programming or flow-based programming, where data flows through the application in a single direction — there are no two-way bindings.


Reducers 不应该相互依赖,因为 redux 并不能确保它们执行的特定顺序。您可以使用 combineReducer 解决此问题。 .您不能确定 extraReducerssetUserAfterSignIn 之后执行 reducer 。
您拥有的选项是:
  • 将更新axios拦截器的代码放在setUserAfterSignIn中 reducer 。
     setUserAfterSignIn: (state, action: PayloadAction<SignInResult>) => {
    // some logic...
    state.signedInUser = {...}
    // update axios
    }
  • 创建 axios 拦截器并将其传递给连接到商店的供应商。通过这种方式,您可以轻松替换 token 的提供方式。
     const tokenSupplier = () => store.getState().signedInUser;

    // ...

    axios.interceptors.request.use(function (config) {
    const token = tokenSupplier();
    config.headers.Authorization = token;

    return config;
    });
  • 提取两个 reducer 函数并确保它们的顺序。
    function signInUser(state, action) {
    state.signedInUser = {...}
    }

    function onUserSignedIn(state, action) {
    // update axios interceptor
    }

    // ....
    // ensure their order in the redux reducer.

    setUserAfterSignIn: (state, action: PayloadAction<SignInResult>) => {
    signInUser(state, action);
    onUserSignedIn(state, action)
    }

  • 编辑

    Given this architecture, What are my options if I have another slice that needs to react when signedInUser has changed?


    我猜你不会喜欢这个答案。前段时间我也遇到了同样的问题。
    另一个切片是商店中的一个独立部分。您可以添加额外的 reducer 来监听来自其他切片的操作,但您无法确定其他切片的 reducer 是否已经更新了状态。
    假设您有一个切片 A和 reducer RA和一片 B带 reducer RB .如果状态 B取决于 A表示 reducer RB应该在任何时候执行 A变化。
    您可以 RA调用 RB ,但这引入了对 RB 的依赖.如果 RA 就好了可以发送类似 { type: "stateAChanged", payload: stateA} 的 Action 以便其他切片可以监听该 Action ,但 reducer 无法分派(dispatch) Action 。您可以实现一个中间件,通过调度程序来增强操作。例如。
    function augmentAction(store, action) {
    action.dispatch = (a) => {
    store.dispatch(a)
    }
    store.dispatch(action)
    }
    以便 reducer 可以分派(dispatch) Action 。
    setUserAfterSignIn: (state, action: PayloadAction<SignInResult>) => {
    // some logic...
    state.signedInUser = {...}
    action.dispatch({type : "userSignedIn": payload: {...state}})
    }
    但是这种方法不是标准方法,如果过度使用它,可能会引入导致调度中无限循环的循环。
    有些人不使用不同的切片,而是使用不同的商店并使用商店的订阅连接它们。这是官方的AP,但是如果你不够注意它也会引入循环。
    所以最后最简单的方法就是调用 RB来自 RA .您可以通过反转它来稍微管理它们之间的依赖关系。例如。
    const onUserSignedIn = (token) => someOtherReducer(state, { type: "updateAxios", payload: token});

    setUserAfterSignIn: (state, action: PayloadAction<SignInResult>) => {
    // some logic...
    state.signedInUser = {...}
    onUserSignedIn(state.signedInUser.token)
    }
    现在您可以更换 onUserSignedIn测试中的回调或使用调用其他注册回调的复合函数。
    编辑
    我目前正在处理 中间件库来解决我们的问题。我在 Github 上发布了我的图书馆的实际版本和 npm .这个想法是你描述状态和 Action 之间的依赖关系,这些依赖关系应该在变化时分派(dispatch)。
    stateChangeMiddleware
    .whenStateChanges((state) => state.counter)
    .thenDispatch({ type: "text", payload: "changed" });

    关于reactjs - 如何在 Redux Toolkit 中订阅 React 组件之外的状态?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69053186/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com