Redux is a useful JavaScript library that allows you manage state in your application. Through the use of Thunk middleware, you can even use it to fill your store with data from calls to an API. For this article, I'll walk through how I used Redux to track state in my Content Tracker React application. I'll be removing extraneous code snippets, so if you're interested in seeing everything, visit my Github!
What's in Store
The state of your application will be located in the Redux store. In order to create this variable, we'll need to install a few dependencies via node package manager (or yarn) to have access to all of the functions needed to take advantage Redux. Both of the following are required: redux
and react-redux
. The middleware redux-thunk
should also be installed if your store requires asynchronous logic (I'll be using this in my examples).
//index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { BrowserRouter as Router} from 'react-router-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import reducer from './reducers/Reducer';
const store = createStore(reducer, applyMiddleware(thunk));
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<Router>
<App />
</Router>
</Provider>
</React.StrictMode>,
document.getElementById('root')
);
We create our store using the createStore
method from Redux, which takes two arguments: our future reducer, and a method to apply our Thunk middleware. We use the <Provider />
component with our store as a property to allow its children access to our store and thus our application's state.
Reducio!
Our store requires us to create a reducer that will take our current state and an action and "reduce" these to create our new application state. One principle to follow is that our state should be immutable. That is, our new state will be derived from a copy of our previous state and will reflect the change based on our actions type. An action is an object with a type key and some kind of payload/data that will be needed to create our new state. In my application's backend, each Content had many Reviews however I normalized this setup in my app's state. Rather than having nested data, I had two arrays (one for each), two reducers, and utilized Redux's combineReducers
method which sends the relevant action to the correct reducer.
//Reducer.js
import { combineReducers } from "redux";
const reducer = combineReducers({
contents: contentsReducer,
reviews: reviewsReducer
});
export default reducer;
function contentsReducer(state = [], action) {
switch (action.type) {
case "GET_CONTENT":
return [...state, ...action.contents]
case "CREATE_CONTENT":
return[...state, action.content];
case "DELETE_CONTENT":
return[...state].filter(elem => elem.id !== action.payload);
default:
return state;
}
}
function reviewsReducer...
Dispatch, not Datpatch
Now we've gotten to the part where we connect one of our React components to the store. To do this, we'll be using the useSelector
and useDispatch
hooks from react-redux
. The former allows us to connect to our store's state while the latter allows us to connect to our dispatch function from our store. We'll be dispatching actions to go from our current state to our new state in our component. I used the useEffects
hook from react
to dispatch my fetchContents
action when the Contents List component mounted.
//contentActions.js
export const fetchContents = () => {
return (dispatch) => {
fetch("http://localhost:3000/contents")
.then(response => response.json())
.then(data => {
dispatch({ type: "GET_CONTENT", contents: data });
})
.catch(fail => alert(fail));
};
};
//ContentList.js
import { useSelector, useDispatch } from 'react-redux';
import { useEffect } from 'react';
import { fetchContents } from '../actions/contentActions';
export default function ContentList() {
const contents = useSelector((state) => state.contents)
const dispatch = useDispatch()
useEffect(() => {
if (contents.length === 0) {
dispatch(fetchContents())
}
}, [])
Conclusion
In summary, you'll need to connect your application to the store, create a reducer based on how you'd like to store the data in your application state, and define the actions that will be be triggered based on changes/input to your components. Hopefully this article was helpful in using Redux to manage the state of your React application!
Top comments (0)