Skip to content

Instantly share code, notes, and snippets.

@wufe
Created November 14, 2016 13:58
Show Gist options
  • Save wufe/5796d89f278a7ec230b6ac7abdab7497 to your computer and use it in GitHub Desktop.
Save wufe/5796d89f278a7ec230b6ac7abdab7497 to your computer and use it in GitHub Desktop.
Redux summary

This file is intended as a reminder for Redux practices

1- The Store

Store is created using a configureStore snippet, which is

import {createStore, applyMiddleware, compose} from 'redux';
import rootReducer from '../reducers';
import reduxImmutableStateInvariant from 'redux-immutable-state-invariant';

const store = createStore(
    rootReducer,
    initialState,
    compose(
        applyMiddleware(reduxImmutableStateInvariant()),
        window.devToolsExtension ? window.devToolsExtension() : f => f
    )
);

The rootReducer used while composing the Store, is defined as a combination of all reducers in the app:

Every reducer gets the chance to define a new state, listening for actions and filtering them by their type, creating new layers of the global state object.

2- The reducers' index aka rootReducer

import {combineReducers} from 'redux';
import appState from './appStateReducer';

const rootReducer = combineReducers({
    appState
});

Simply takes every reducer available and combine them into a single giant reducer capable of doing great things.

3- The single reducer

i.e. appStateReducer

export default ((state = [], action) => {
    switch( action.type ){
        case "PAGE_LOADING":
            return Object.assign({}, state, { pageLoading: action.loading });
        default:
            return state;
    }
})

Using the immutability of Object.assign(), creates a new state and replaces it in the Store.

4- The actions module

i.e. appActions.js

export function setPageLoading(){
    return { type: 'PAGE_LOADING', loading: true };
}
export function setPageLoaded(){
    return { type: 'PAGE_LOADING', loading: false };
}
export function setState(state){
    return { type: 'PAGE_LOADING', loading: state };
}

5- The connect decorator (smart react-redux connection)

import React from 'react';
import Style from '../../styles/pageLoadingBar.scss';
import {connect} from 'react-redux';
import * as appActions from '../../actions/appActions';

class PageLoadingBar extends React.Component{
    constructor(props, context){
        super(props, context);
        this.state = { visible: false };
    }

    render(){
        return (
            <div
                className="pageLoadingBar"
                style={{opacity: this.props.appState.pageLoading ? 1 : 0}}
            >
                <div className="loadingBar">
                </div>
            </div>
        );
    }
}

function mapStateToProps(state, ownProps){
    return {
        appState: state.appState
    };
}

function mapDispatchToProps(dispatch){
    return {
        setLoading: () => dispatch(appActions.setPageLoading()),
        setLoaded: () => dispatch(appActions.setPageLoaded()),
        setState: loading => dispatch(appActions.setState(loading))
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(PageLoadingBar);

The decorations happens in the last line, using the connect function imported from 'react-redux'.
Uses a mapStateToProps function that has to have two parameters: the global state of the app, and the props of the component.
The global state can be used to access the keys of the global state stored into the Store and to be used into the props of the decorated component using a custom key.
In this example appState: state.appState means that accessing this.props.appState from the component, you can have access to (global) state.appState defined in the Store and named after the rootReducer convention.
The mapDispatchToProps maps every key of the returning array into the props of the component. In the example if a this.props.setLoading is called in the component, the action will be automatically dispatched.
If no mapDispatchToProps is defined, a dispatch prop will be attached to the component, to be able to dispatch an action manually when firing an action.

An alternative way to bind actions to the dispatcher is using bindActionCreators.
e.g.

import {bindActionCreators} from 'redux';
function mapDispatchToProps(dispatch){
    return {
        actions: bindActionCreators(appActions, dispatch)
    }
}

This will let you use this.props.actions.<actionName> in the component in order to access to the desired action.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment