4

If I want to dispatch an action whenever a piece of state changes, currently how I do this is by listening to that state inside a component useEffect hook and dispatching an action whenever that state changes.

For example, let's say I have the following slice:

export const updateBar = createAsyncThunk("mySlice/updateBar", async () => { const { data } = await axios.get("/some/api"); return data; }); const mySlice = createSlice({ name: "mySlice", initialState: { foo: false, bar: 0, }, reducers: { updateFoo: (state, { payload }) => { state.foo = payload; }, }, extraReducers: { [updateBar.fulfilled]: (state, { payload }) => { state.bar = payload; }, }, }); 

Now if I want to update bar whenever foo changes, I have to go to the component side to add the following code:

const Component = () => { const foo = useSelector((state) => state.mySlice.foo); const dispatch = useDispatch(); useEffect(() => { dispatch(updateBar()); }, [dispatch, foo]); return <div></div>; }; 

I am wondering is it possible to call updateBar whenever foo changes within the redux slice and without having to touch the component side, because I think that it would be cleaner if all state side effects are abstracted away from the component.

1

1 Answer 1

1

One solution is to create another thunk encapsulating the two update operations. We do the update foo operation in this thunk and use thunkAPI to dispatch update bar action.

We can continue to keep updateFoo action when you just want to update foo.

Thunk helps us encapsulate the logic of actions. Such as when are actions dispatched, what actions are dispatched, and their relationship, serial dispatch or concurrent dispatch.

E.g.

//@ts-nocheck import { createAsyncThunk, createSlice, configureStore } from '@reduxjs/toolkit'; const mockApi = async () => { return { data: 100 }; }; export const updateBar = createAsyncThunk('mySlice/updateBar', async () => { const { data } = await mockApi(); return data; }); export const updateFooWithBar = createAsyncThunk('mySlice/updateFooWithBar', (arg, thunkAPI) => { // If bar depends on foo state or other state, // you can use thunkAPI.getState() to get the whole state, pick what you need. thunkAPI.dispatch(updateBar()); return arg; }); const mySlice = createSlice({ name: 'mySlice', initialState: { foo: false, bar: 0, }, reducers: { updateFoo: (state, { payload }) => { state.foo = payload; }, }, extraReducers: { [updateBar.fulfilled]: (state, { payload }) => { state.bar = payload; }, [updateFooWithBar.fulfilled]: (state, { payload }) => { state.foo = payload; }, }, }); const store = configureStore({ reducer: mySlice.reducer }); store.subscribe(() => { console.log(store.getState()); }); store.dispatch(updateFooWithBar('next foo')).then(() => { store.dispatch(mySlice.actions.updateFoo('next next foo')); }); 

Execution result:

{ foo: false, bar: 0 } { foo: false, bar: 0 } { foo: 'next foo', bar: 0 } { foo: 'next foo', bar: 100 } { foo: 'next next foo', bar: 100 } 
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the suggestion. But what if I have multiple actions that update foo? For example, if foo was an array and I have actions such as addToFoo, removeFromFoo, clearFoo, etc. Then I would need to remember to call dispatch(updateBar()) in every action that updates foo or else it might introduce a potentially difficult-to-find bug.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.