我有以下切片:
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
更改( setUserAfterSignIn
和 signOut
),内部 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 .
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
解决此问题。 .您不能确定 extraReducers
在 setUserAfterSignIn
之后执行 reducer 。您拥有的选项是:
setUserAfterSignIn
中 reducer 。 setUserAfterSignIn: (state, action: PayloadAction<SignInResult>) => {
// some logic...
state.signedInUser = {...}
// update axios
}
const tokenSupplier = () => store.getState().signedInUser;
// ...
axios.interceptors.request.use(function (config) {
const token = tokenSupplier();
config.headers.Authorization = token;
return config;
});
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/