Blogs

Getting started with Redux: Redux with React.

04

19 Nov

Samah Gaber

7 min read.

Why using Redux in React?

In React, each component has its own state. It worked just fine within that component, but then we faced some issues with this approach:

  1. The state was enclosed inside of that component and couldn’t be reached from outside of it:
  • If we needed to pass that state to another component, we could only pass it along as props from parent to child component.
  • If we needed to pass the state between siblings or unrelated components, we would have had to lift the state up the component tree to a higher component so that we can pass it down to both of the desired components.
  1. If we needed to alter the state from outside of the component then child components can receive callback functions in the props from their parent component. These functions can be used to alter the local state of the parent component.
  2. Even if we could pass the state to children they don’t know that these props represent the state of their parents. They only receive the values and handle callback functions without knowing their source.

So, with Redux we could separate the state in a global container or storage so that it would be accessible by any component in the app.

Installing Redux and React-Redux:

As we know, Redux wasn’t designed to work only with React. That’s why we are using React-Redux library as it contains many functions to facilitate the communication between Redux and React. Together we’re going to create a very simple app that fetches a list of posts and renders them on the screen.

  • Open up your terminal and let’s start by creating a new React app:

create-react-app react-redux-app

  • Now change directory to your project folder:

cd react-redux-app

  • Next, install both Redux and react-Redux library:

npm install –save redux react-redux

  • Now you can start your application:

npm start

  • In your src/ directory delete all of the boilerplate files that come with create-react-app. And we are going to add our own files.

cd src

rm logo.svg App.js App.test.js App.css

  • now create the following folders and files so that the app folders structure would look like this:

-src/

–app/

—App.js

–components/

—Button.js

—PostsList.js

—SinglePost.js

–redux/

—actions/

—-index.js

—reducers/

—-index.js

—-postsReducer.js

—actionTypes.js

–store/

—index.js

–index.js

–postsList.js

React-Redux structure:

Before we can go on coding, let me explain the structure of our app upon using Redux.

In the above diagram we can see that React-Redux library provides 2 components; Provider and Connect. We are not going to create them as they are already implemented by React-Redux.

We learned previously that after combining reducers we create what’s so called Redux Store that contains all of the reducers and the state of the application. We are going to take that store and pass it as a prop to the Provider which will be rendered at the top of our component tree, wrapping our App component.

The Provider can be considered as a reference to the Redux Store and it’s the only component that has direct access to the state. Other components can’t deal with the Redux Store nor the Provider directly. Components can only deal with the Provider through the Connect component.

Now enough talking and let’s see some code to get a clearer picture. In your index.js:

import React from ‘react’;

import ReactDOM from ‘react-dom’;

import { Provider } from ‘react-redux’;

import App from ‘./app/App’;

import Store from ‘./store’;

ReactDOM.render(

    <Provider store={Store}>

        <App />

    </Provider>,

    document.getElementById(‘root’)

);

As you can see we imported Provider from react-redux, used it to wrap our app component and we passed it the Store we are going to create next.

Create Redux Store:

In src/store/index.js

import { createStore, compose } from ‘redux’;

import reducers from ‘../redux/reducers’;

const Store = createStore( reducers );

export default Store;

Here we have imported our combined reducers from redux/reducers/index.js and then created our Redux Store and passed it the combined reducers.

Creating Reducers:

In src/redux/reducers/postsReducer.js you can create your first reducer

export default ( state = {}, action) => {

     switch( action.type ) {

           case ‘FETCH_POSTS_LIST’:

                 return { …state, postsList: action.payload };

           default:

                 return state;

      };

};

It’s a very simple reducer that checks on the action.type and returns state with the new posts list. You must return the state in the default otherwise it will give you an error.

As you can see we have used the action.type as a string but it’s recommended that we store all of our action types used all over the app as variables in one file instead of the strings to eliminate any risk of spelling mistakes.

So, in you src/redux/actionTypes.js

export const FETCH_POSTS_LIST = ‘FETCH_POSTS_LIST’;

So now you can replace the string in your reducer with our new variable

import { FETCH_POSTS_LIST } from ‘../actionTypes’;

export default ( state = {}, action) => {

   switch( action.type ) {

      case FETCH_POSTS_LIST:

          return { …state, postsList: action.payload };

      default:

          return state;

   };

};

To combine the reducers in your src/redux/reducers/index.js:

import { combineReducers } from ‘redux’;

import PostsReducer from ‘./postsReducer’;

export default combineReducers({

     postsList: PostsReducer

});

Creating Actions:

In src/redux/actions/index.js create the first action

import { FETCH_POSTS_LIST } from ‘../actionTypes’;

export const fetchPosts = payload => ({

  type: FETCH_POSTS_LIST,

  payload

});

Building our app:

  1. App component.

In src/app/App.js:

import React from ‘react’;

import Button from ‘../components/Button’;

import PostsList from ‘../components/PostsList’;

const App = () => {

    return(

        <div>

           <Button />

           <PostsList />

        </div>

    );

};

export default App;

  1. Button component ( mapDispatchToProps )

In src/components/Button.js

import React from ‘react’;

import { connect } from ‘react-redux’;

import { fetchPosts } from ‘../redux/actions’;

import { postsList } from ‘../postsList’;

class Button extends React.Component {

     onClickFetchPosts = () => {

         this.props.onFetchPosts(postsList);

     };

     render() {

        return(

             <button onClick={this.onClickFetchPosts}>

                   Get Posts

             </button>

        );

     };

};

const mapDispatchToProps = dispatch => ({

    onFetchPosts: postsList => dispatch(fetchPosts(postsList))

});

export default connect(null, mapDispatchToProps)(Button);

 

Let’s pause for a second to explain what we have done in this component. Like we have said before, that we use Connect to connect components to the Store. And that’s exactly what we have done here. we imported connect (with lowercase c) from react-redux and wrapped the button with it.

Then we passed to connect a method called mapDispatchToProps that gives access to the dispatch method of the Redux store. It takes dispatch function as a parameter and returns an object with key (custom name of your choice) and the value is a callback function that dispatches our action.

So, now to call our action creator in the component we can say: this.props.keyName().

There is another way instead of using mapDispatchToProps, which is by passing the action creator directly in an object to connect like the following:

export default connect(null, { fetchPosts })(Button);

And then we call it directly in our methods like this:

onClickFetchPosts = () => {

  this.props.fetchPosts(postsList);

};

The two ways are absolutely correct and use whichever of them you like.

P.S. We passed the action creator a fake postsList that we created instead of calling a real api and fetching real data.

In src/postsList.js

export const postsList = [

{

  “userId”: 1,

  “id”: 1,

  “title”: “excepturi”,

  “body”: “nostrum rerum est autem sunt rem eveniet architecto”

},

{

  “userId”: 1,

  “id”: 2,

  “title”: “esse”,

  “body”: “qui aperiam non debitis possimus qui neque nisi nulla”

},

{

  “userId”: 1,

  “id”: 3,

  “title”: “exercitationem”,

  “body”: “molestiae porro eius odio et labore et velit aut”

},

{

  “userId”: 1,

  “id”: 4,

  “title”: “occaecati”,

  “body”: “quis sunt voluptatem rerum illo velit”

},

{

  “userId”: 1,

  “id”: 5,

  “title”: “nesciunt”,

  “body”: “est aut tenetur dolor neque”

}]

  1. PostsList component ( mapStateToProps )

In src/components/PostsList.js

import React from ‘react’;

import { connect } from ‘react-redux’;

import SinglePost from ‘./SinglePost’;

class PostsList extends React.Component {

  render() {

     const { postsList } = this.props;

     return(

        <div>

           { postsList && postsList.length > 0 ? postsList.map(

                  singlePost =>

                       <SinglePost

                           post={singlePost}

                           key={singlePost.id}/>

              ) : <p>Loading posts … </p> }

        </div>

     )

  }

};

const mapStateToProps = state => ({

     postsList : state.postsList.postsList

});

export default connect(mapStateToProps)(PostsList);

Like in Button where we used connect to dispatch our action, we’re using it here to fetch some data from the Store.

We passed to connect another method called mapStateToProps, which gives the component the access to the Store and get whatever data in state it needs through its props. It takes the state as a parameter and returns an object with custom key name and a value of the desired state keys.

If you added console.log(state) inside mapStateToProps, you would see the actual state with the keys we created in combined reducers and with the postsList that we dispatched in our action.

So now we can access this data through this.props.keyName.

P.S. if there isn’t mapStateToProps in the component we pass “null” to the connect like we’ve done in the Button component.

Now we can say that we are finished with the full Redux cycle inside our React app. The last part is only about rendering the posts more nicely.

  1. SinglePost component

In src/components/SinglePost.js

import React from ‘react’;

const SinglePost = props => {

  const { title, body } = props.post;

  return(

     <div>

        <h3>{title}</h3>

        <p>{body}</p>

        <hr/>

     </div>

  )

};

export default SinglePost;

 

Summary

  1. Why use Redux with React?
  2. Installing redux and react-redux libraries.
  3. Using Provider and connect.
  4. Building Redux store, reducers and action creators.
  5. Using mapDispatchToProps and mapStateToProps.

 

That’s it for now. Hope the article was helpful for you 🙂

Comments