Skip to content

Build Your Own State Container

Howie Reith edited this page Sep 27, 2019 · 7 revisions

The next step is to examine Redux. However, before diving straight in, it's important to understand what Redux is trying to accomplish. This section demonstrates a simple state container that mimics the core functionality of Redux. It's curious how little code is required to accomplish this.

Exercise 5:

  1. Navigate to the state-container directory and run the following commands:
npm install
npm start
  1. Once the app loads, play the `Pirate Tic Tac Toe game to familiarize yourself with the app

  2. Open the state-container folder using your favorite editor (again, VIM...) and familiarize yourself with the application

Important Concepts

  • The core functionality of Redux can be reproduced in 13 lines of code as demonstrated in the Store.js file.

    • The entire state in the store is held in the internalState private variable.
    • The store accepts a reducer that maps logic to intents. This is the only means of mutating the store's state. In Redux parlance, an intent in an action.
    • The subscribe method accepts functions that will execute after every dispatch event. This is the typical mechanism by which Redux informs React that is should re-render the application.
    • The getState method simply returns the application state.
    // Store.js
    export const createStore = (reducer, defaultState) => {
      let internalState = defaultState;
      let handlers = [];
    
      return {
        dispatch: (intent) => {
          internalState = reducer(internalState, intent);
          handlers.forEach(h => h());
        },
        subscribe: handler => handlers.push(handler),
        getState: () => internalState,
      };
    };
  • The StateContainer.js file is where the intents for the Pirate Tic Tac Toe application are defined. Each intent accepts a model and returns a new model. The model itself is never actually mutated, it's replaced with a new model.

    // StateContainer.js
    const intents = {
      PLAY: (model, intent) => {
        ...
        return {...model, player, winner, gameBoard};
      },
      RESET_GAME: model => {
        ...
        return {...model, gameBoard: emptyBoard, winner: null};
      },
    };
  • The application must instantiate a single global container to be used everywhere within the application.

    // StateContainer.js
    const update = (model, intent) => {
      let action = intents[intent.type];
      if (!action) return model;
    
      return action(model, intent);
    };
    
    var container = createStore(update, defaultState);
  • Intents are joined with end user action in the view.

    // GameSquare.js
      onClick={() => {
        StateContainer.dispatch({
          type: 'PLAY',
          row: props.row,
          square: props.square,
        });
      }}>
    
    // ResetGame.js
      <button onClick={() => StateContainer.dispatch({type: 'RESET_GAME'})}>
        New Game
      </button>

Quiz 3

What pattern does the Custom State Container adhere to?

  • Observer
  • State Machine
  • Publisher/Subscriber
  • Singleton

Answers

Exercise 6:

  1. Convert the state-container application to a Redux application via the: following two steps
    • Navigate to the state-container directory and run the following command.
    npm i --save redux
    • Change the import at the top of the 'StateContainer.js` file.
    - import {createStore} from './Store';
    + import {createStore} from 'redux';
  2. Run the application and marvel at your genius.

There are no pros and cons listed in this section because it's not advisable to build a custom state container. This section is meant to merely convey the concept about how React works under the hood.

Next