React + Redux: Managing State

Store

Redux allows us to manage state within our app. Lets starts by installing redux by using npm:

npm install --save redux
npm install --save-dev babel-plugin-transform-class-properties

In our app folder, lets create a folder called redux and another folder inside redux called modules. Create a js file called users inside modules.

In our users.js file we need to specify:

  • constants (in case we would like to re-use our action types)
  • action creators
  • initial states
  • reducers

Example:

const AUTH_USER = 'AUTH_USER'
const FETCHING_USER = 'FETCHING_USER'
const FETCHING_USER_SUCCESS = 'FETCHING_USER_SUCCESS'

// Action Creators
function authUser (uid) {
  return {
    type: AUTH_USER,
    uid,
  }
}

function fetchingUser () {
  return {
    type: FETCHING_USER,
  }
}

function fetchingUserSuccess (uid, user, timestamp) {
  return {
    type: FETCHING_USER_SUCCESS,
    uid,
    user,
    timestamp,
  }
}


// Initial States
const initialUserState = {
  lastUpdated: 0,
  info: {
    name: '',
    uid: '',
  },
}

const initialState = {
  isFetching: false,
  isAuthenticated: false,
  authenticatedId: '',
}

// Reducers
function user (state = initialUserState, action) {
  switch (action.type) {
    case FETCHING_USER_SUCCESS :
      return {
        ...state,
        info: action.user,
        lastUpdated: action.timestamp,
      }
    default :
      return state
  }
}

export default function users (state = initialState, action) {
  switch (action.type) {
    case AUTH_USER :
      return {
        ...state,
        isAuthed: true,
        authedId: action.uid,
      }
    case FETCHING_USER:
      return {
        ...state,
        isFetching: true,
      }
    case FETCHING_USER_SUCCESS:
      return action.user === null
        ? {
          ...state,
          isFetching: false,
        }
        : {
          ...state,
          isFetching: false,
          [action.uid]: user(state[action.uid], action),
        }
    default :
      return state
  }
}

In our main index.js file add:

import { createStore } from 'redux'
import users from 'redux/modules/users'
const store = createStore(users);
console.log(store);

Run the newly saved code and check the console to analyse the content of our store. Make sure to remove the console.log once analysed.

Screen Shot 2018-02-13 at 12.19.42

As you can see above, dispatch takes in an action. This means that if we want to tell our redux store that something has changed in our state, we dispatch an action using our action creator functions. Example:

dispatch(authUser('username'))

getState will as you can imagine, return our initial state that we specified in users.js above.

Providers

So far, we managed to take state out of our components but now we need to give our components a way to access state in our redux. This is what providers do.

npm install --save react-redux

In our main index.js file that we used above to create our store, import Provider and wrap all you routes / elements in passing it our newly created store above:

import { Provider } from 'react-redux'

ReactDOM.render(
  
    {routes}
  ,
  document.getElementById('app')
)

Connect

Connect will be used to connect our AuthenticateContainer with our redux store. When exporting our class, we will now invoke connect() which returns a function excepting AuthenticateContainer as its first argument.

before:

export default AuthenticateContainer

after:

export default connect(mapStateToProps, mapDispatchToProps)(AuthenticateContainer)

Connect accepts 2 arguments. The first argument tells redux which parts of the state does this component care about by using mapStateToProps.

function mapStateToProps (state) {
  console.log('state', state);
  return {
    isFetching: state.isFetching,
    error: state.error
  }
}

Our second argument will help our component dispatch actions from user.js

function mapDispatchToProps (dispatch) {
  return bindActionCreators(userActionCreators, dispatch)
}

In order to use bindActionCreators and the user actions we defined, we will need to import them at the top of our component.

import { bindActionCreators } from 'redux'
import * as userActionCreators from 'redux/modules/users'

bindActionCreators will help us bind the dispatch call. If we do not use bindActionCreators we would need to wrap any function in users.js in a dispatch function as follows:

this.props.dispatch(userActionCreators.authUser(user.uid))

but … when we use bindActionCreators in our mapDispatchToProps, we simply call:

this.props.authUser(user.uid)

Multiple Reducers

When having more than one reducer, for instance ‘users’ and ‘books’, we need to use combineReducers from redux.

import { createStore, combineReducers } from 'redux'
import { Provider } from 'react-redux'
import * as reducers from 'redux/modules'

const store = createStore(combineReducers(reducers))

Create all reducer files in one folder and import * as reducers. When creating a store we will then pass all reducers as a parameter of combineReducers.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s