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.
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.