React Redux Readable Project

 

The main purpose of this project was to learn how to properly use Redux in my React applications. The main real benefit I got from redux is bypassing having to use what I call “Bridge Functions” to pass state to the base component for re-rendering.

Store

So the first thing I learned about here is the store. The store essentially acts as the single source of truth for the whole application. Before Redux I was essentially using the base app component as the state as a defacto store (single source of truth) but that required many bridge functions to pass state all the way up to the base component if I had many nested components. The store holds all of the apps information and is able to do this as it is passed into a Provider component that is wrapped around the base component. Here is the sample code from the application:

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__  || compose;

const store = createStore(
    reducer,
    composeEnhancers(
        applyMiddleware(logger)
    )
);

ReactDOM.render(
    <Provider store={store}>
        <BrowserRouter>
            <App store={store}/>
        </BrowserRouter>
    </Provider>, document.getElementById('root')

);
registerServiceWorker();

As you can see the App component is wrapped by the provider component. You can also see that the createStore method takes in a reducer method, which we will go over now…

Reducer

The reducer is basically a pure function (or a series of pure functions) that take the original state and the action (we will go over that next) and return what the updated state should be after the update. It is important note that the reducer does not change the state on its own, it just informs the store how it should update the state. Here is an example of a shorter reducer function that is responsible for the modal state of the application:

//reducer to change modal state
function modal(state = {openState: false, postID: null}, action) {
    const {openState, postID} = action;

    switch (action.type) {
        case CHANGE:
            return {
                ...state,
                openState: openState,
                postID: postID
            };
        default:
            return state;
    }
}

export default combineReducers({
    posts,
    category,
    modal
});

You’ll also note that we are not exporting just the modal reducer but also the posts and category reducers (not shown) using the combineReducers method.

Actions

As you can see from above that these reducers take in actions. Actions are basically functions that inform the reducer what type of action will be taking place and it defines the type of data that will need to be passed in order to perform the action. Here is an example of one of our actions:

export const ADD_COMMENT = 'ADD_COMMENT';

export function addComment({timestamp, body, author, parentId}) {
    return {
        type: ADD_COMMENT,
        timestamp,
        body,
        author,
        parentId
    }
}

This is an action that will add a comment, you can see all of the information that is required to do this is the timestamp of the comment, the body content, the author and the parent posts id. You will also note that we define the action label as a constant just for consistency.

Dispatching Actions

Here is where the magic comes in, actually updating the store rather than using bridge functions to pass state. To do this you need to dispatch actions to the store (via the reducer). The first step is to create a mapDispatchToProps method:

function mapDispatchToProps (dispatch) {
    return {
        addPost: (data) => dispatch(addPost(data)),
        editPost: (data) => dispatch(editPost(data)),
        closeModal: (data) => dispatch(changeModalState(data))
    }
}
export default connect(
    mapDispatchToProps
)(PostForm);

You’ll see that instead of just exporting the PostForm component (the example component we are using) we use the connect method and pass in mapDispatchToProps function into it, then curry the PostForm component, this essentially connects the component to the store.

Next we just do something like this:

ReadableAPI.addPost(this.state.titleValue, this.state.textValue, this.state.authorValue, this.state.category).then((res) => {
                let data = {
                    id: res.id,
                    timestamp: res.timestamp,
                    body: res.body,
                    author: res.author,
                    category: res.category,
                    title: res.title
                }
                this.props.dispatch(addPost(data));
                this.clearValues();
            });

Here you can see that we first do a call to the node server to do add the post, then once thats done we dispatch the action to the reducer to pass to the store and we are done. Normally that could have taken 4 or 5 bridge functions to pass the state all the way up to the base component where a number of things could go wrong in the process, this simplifies things significantly.

If you would like to see the full project you can view or download it here: https://github.com/djarrin/Readable