DEV Community

Antonio Carter
Antonio Carter

Posted on

Connecting Redux to Your React Application

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')
);
Enter fullscreen mode Exit fullscreen mode

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...
Enter fullscreen mode Exit fullscreen mode

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));
  };
};
Enter fullscreen mode Exit fullscreen mode
//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())
    }
  }, [])
Enter fullscreen mode Exit fullscreen mode

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)