Redux-Thunk (usage with redux-toolkit)
Although actions in Redux sound like they do the actions, they are just plain objects. Redux-Thunk is a middleware that allows action creators to be functions. Moreover, Redux-Thunk also enables the action creator functions to be not pure functions, in other words those functions can have side effects possibly involving in asynchronous API calls. While it provides a way to make them asynchronous functions, reducers still stay synchronous.
Thunk functions can delay actions until some events occur. They return functions which return actions, rather than returning actions immediately. We can delay dispatching of an action until some http events happen with the help of Redux-Thunk.
I used redux-toolkit in my example project ( I will give more details in another story about redux-toolkit) which provides easiness in writing redux projects and also speeds up the development.
Redux toolkit creates action creators automatically with reducer methods. While generating thunk functions, our return function should receive dispatch as a parameter. With dispatch parameter we can dispatch the actions we want to perform, before or after some asynchronous events and side effects.
In the example below, we have a counter and an information bar. When we change the counter, we update our counter value on firebase database and when the fetch operation finishes, we update our notification according to the status which is maintained by redux.
App.js
import "./App.css";
import Counter from "./components/Counter";
import StatusBar from "./components/StatusBar";
function App() {
return (
<div className="App">
<Counter />
<StatusBar />
</div>
);
}
export default App;
redux store files
index.js
import { configureStore } from '@reduxjs/toolkit';
import counterSlice from './counter-slice';
import notificationSlice from './notification-slice';
const store = configureStore({
reducer: { counter: counterSlice.reducer, notification: notificationSlice.reducer },
});
export default store;
counter-slice.js
import { createSlice } from "@reduxjs/toolkit";
import { notificationActions } from "./notification-slice";
const counterSlice = createSlice({
name: "counter",
initialState: {
value: 0,
},
reducers: {
updateCounter(state, action) {
if (action.payload === "inc") {
state.value++;
} else if (action.payload === "dec") {
state.value--;
}
},
},
});
export const changeCounter = (val, type) => {
return async (dispatch) => {
dispatch(notificationActions.updateNotification("sending"));
const sendReq = async () => {
const response = await fetch(
"https://mediumprojects-f255b-default-rtdb.firebaseio.com/counter.json",
{
method: "PUT",
body: JSON.stringify({ value: val }),
}
);
if (!response.ok) {
throw new Error("Sending counter value failed.");
}
};
try {
await sendReq();
dispatch(counterActions.updateCounter(type));
dispatch(notificationActions.updateNotification("sent"));
} catch (error) {
notificationActions.updateNotification("error");
}
};
};
export const counterActions = counterSlice.actions;
export default counterSlice;
notification-slice.js
import { createSlice } from "@reduxjs/toolkit";
const notificationSlice = createSlice({
name: "notification",
initialState: {
status: null
},
reducers: {
updateNotification(state, action) {
state.status = action.payload;
},
},
});
export const notificationActions = notificationSlice.actions;
export default notificationSlice;
Counter.js
import classes from "./Counter.module.css";
import { useSelector, useDispatch } from "react-redux";
import { changeCounter } from "../store/counter-slice";
const Counter = (props) => {
const counter = useSelector((state) => state.counter);
const dispatch = useDispatch();
return (
<div className={classes.content}>
<div className={classes.buttons}>
<button
className={classes.button}
onClick={() => {
dispatch(changeCounter(counter.value + 1, "inc"));
}}
>
increment
</button>
<button
className={classes.button}
onClick={() => {
dispatch(changeCounter(counter.value - 1, "dec"));
}}
>
decrement
</button>
</div>
<div className={classes.counter}>counter: {counter.value}</div>
</div>
);
};
export default Counter;
StatusBar.js
import { useSelector } from "react-redux";
import classes from "./StatusBar.module.css";
const StatusBar = (props) => {
const notification = useSelector((state) => state.notification);
return <div className={classes.status}>{notification.status}</div>;
};
export default StatusBar;
You can find the whole project on github
https://github.com/sagmer/redux-thunk
Thank you for reading :)