Updating my React Sudoku Solver app: Replacing Flux with Redux (part 2)

In the first part of converting my Sudoku solver React app from using Flux to Redux I looked at creating the Redux Store, Action Creators, reducers, and connecting Components to the Store. In this part 2 I’m taking a look at using redux-thunk for making my api calls.

Async API calls are integrated into a Redux app by using a ‘thunk’, support is provided by adding react-thunk to your app:

npm install --save redux-thunk

react-thunk is a middleware for your Redux Store. It looks at each Action dispatched to your Store and if it’s a function instead of an Action object it executes it.

Where you create your store with createStore(), add an import for applyMiddleware and thunk:

import { applyMiddleware, createStore } from 'redux';
import thunk from 'redux-thunk';
const middlewares = [thunk];

Pass a call to applyMiddleware as a param to createStore() passing this middlewares array. In my app I’ve already passed the devToolsEnhancer() to createStore():

let store = createStore(puzzleDataReducer, devToolsEnhancer());

Naively, I thought I’d be able to add thunk and devToolsEnhancer() in the array and pass like this:

const middlewares = [thunk, devToolsMiddleware()];
let store = createStore(puzzleDataReducer, applyMiddleware(...middlewares));

… but this leads to some rather cryptic errors and I’ve honestly no idea what this means or how to resolve it:

VM1047:1 Uncaught TypeError: t.apply is not a function
     at :1:47480
     at :1:33759
     at :1:26192
     at eval (redux.js:602)
     at eval (redux.js:646)
     at createStore (redux.js:79)

Luckily a quick search found this article, suggesting the way to combine these enhancers is to use redux’s compose() function. Import compose from redux:

import { createStore, applyMiddleware, compose } from 'redux';

Use compose() to combine thunk and the devToolsEnhancer() call like this:

compose(
   applyMiddleware(thunk, otherMiddleware()),
   window.devToolsExtension ? window.devToolsExtension() : f => f
 )

In my case I don’t have other middleware to include, so my createStore() was originally this:

let store = createStore(puzzleDataReducer, devToolsEnhancer());

and now including thunk and using compose() it looks like this:

let store = createStore(puzzleDataReducer, compose(
    applyMiddleware(thunk),
    window.devToolsExtension ? window.devToolsExtension() : f => f
    )
);

Next I need to move my Flux Action that makes my api call to a function that is returned by an Action Creator. Since react-thunk looks for functions returned by Action Creators instead of Action objects and executes them. this was a relatively minor change moving the function from my original Action to my Action Creator.

At this point I’ve got a working app, migrated from Flux to Redux. You can check the final result on GitHub here. I’ve still got some cleanup to do – in particular I don’t have a clean separation between my reducer code and my Action Creator code. I’ll code back and do some cleanup later, and also deploy a parallel copy of the same app to AWS. Right now the original Flux based app is deployed here. I’ll deploy the final Redux based app shortly.

Updating my React Sudoku Solver app: Replacing Flux with Redux (part 1)

A while back I built a React frontend for a AWS Lambda Sudoku solver. At the time I used Flux (original source for this app is here), but I’m taking a look at updating it to replace Flux with Redux.

As a starting point I took another example React app, a simplest case app using Flux and then converted it to Redux. After working through the changes to update this app to use Redux, I put together a quick cheatsheet of changes here. Here’s both projects for a comparison:

https://github.com/kevinhooke/SimpleReactFluxExample

https://github.com/kevinhooke/SimpleReactReduxExample

Here’s a walkthrough of the main changes to an existing app (summarizing the steps from my cheatsheet, link above, and looking at some of the steps in more detail):

  • install react-redux and react-devtools-extension
  • create a store using Redux createStore, replacing previous Flux store that used Dispatcher, EmitEvent and [add|remove]ChangeListeners
  • create reducers to manipulate the Store
  • wrap root component with <Provider>
  • connect components with the Store

The Redux approach to maintaining state in the Store is significantly different from Flux:

  • Redux has a single store, whereas Flux can have many
  • State maintained in a Redux Store is manipulated with reducer functions
  • Reducer functions take the state to be manipulated and actions that are applied to the state to change it
  • Flux maintains a copy of the state in variables in the Store itself, whereas the current state of the store is passed as an argument to reducers, and a new updated copy is returned as the result

Since there’s a number of changes to the store, it’s worth looking at each of these in more detail.

Updating Flux Store to a Redux Store

Previously my Flux store maintained state in two vars:

var puzzleData = {}
var message = {}

These are no longer needed in the Redux store, but there in an initialState for initializing the Store. This can simply be the same as puzzleData before but just renamed for clarity:

const initialPuzzleData = {}

Next, each of the case statements in the Action can move over to the reducer function, and updated to manipulate the content of the store passed as a param and then return the modified store as the result. For example, previously each action was handled like this:

case 'NEW_DATA' :
  this.setData(action.data);
  this.emit('change');
  break;

With Redux, each case block is now going to look more like this:

case 'NEW_DATA' :
  return Object.assign( {}, action.data );

Create ActionCreators

In Flux, an Action combines a couple of concepts, it’s the payload to be dispatched to the Store as well as the mechanism for performing the dispatch of the Action. With Redux these concepts are separate, and the Action is merely the payload to apply to updating the Store. There is a concept of Action Creators though, that can be called to build the Action payload. Before with Flux we would have an Action function like this:

    initSamplePuzzle(){
        console.log("SudokuSolverAction initSamplePuzzle()");
        var puzzle = {
            rows:
                [
                    ["", "", "", "8", "1", "", "6", "7", ""],
                    ["", "", "7", "4", "9", "", "2", "", "8"],
                    ["", "6", "", "", "5", "", "1", "", "4"],
                    ["1", "", "", "", "", "3", "9", "", ""],
                    ["4", "", "", "", "8", "", "", "", "7"],
                    ["", "", "6", "9", "", "", "", "", "3"],
                    ["9", "", "2", "", "3", "", "", "6", ""],
                    ["6", "", "1", "", "7", "4", "3", "", ""],
                    ["", "3", "4", "", "6", "9", "", "", ""]
                ]
        }

        AppDispatcher.dispatch({
            actionName: 'NEW_DATA',
            data: puzzle.rows
        });
    }

and now this is simplified to build the payload, the Flux Dispatcher is removed, and the function. now just returns the payload to be processed:

    return {
        actionName: 'NEW_DATA',
        data: puzzle.rows
    };

Another simple example that builds an Action object for an update to the Store:

export function updateGrid(payload) {
    return { type: NEW_DATA, payload }
};

Connecting Components to the Store

In each Component, implement mapStateToProps and mapDispatchToProps to pass store and dispatch() as props to each Component, and call connect() to connect each Component with your Redux Store.

Here’s my matchStateToProps:

const mapStateToProps = state => {
    return { 
        grid: state.grid,
        message: state.message
     };
};

And here’s my mapDispatchToProps:

function mapDispatchToProps(dispatch) {
    return {
        updatePuzzleData :  grid => dispatch(updatePuzzleData(grid)),
        clearData : () => dispatch(clearData()),
        initSamplePuzzle : () => dispatch(initSamplePuzzle())
    }
}

mapDispatchToProps is where your Action Creators are called to create Action objects for modifying state, and then dispatched to apply against the state of the Store by your reducers.

Connect your components to your Store with a call to connect() :

const SudokuSolver = connect(mapStateToProps, mapDispatchToProps)(ConnectedSudokuSolver);

export default SudokuSolver;

Part 2

In part 2 I’ll look at moving the api call from the Flux Action to use Redux middleware and thunks.

Interrelated personal side projects can be a goldmine of learning opportunities

Some people struggle to find ideas for working on side/personal software development projects for learning (and fun!). I’ve kept a running list of ideas in an online notebook and if I’m struggling for an idea for a new project I refer back to this list.

Over the past couple of years I stumbled across a series of inter-related projects that’s been a goldmine of learning opportunities, from frontend to backend to serverless.

This series of projects started with a question “Can you solve a problem without understanding the problem?”. tl;dr? No. The problem for this activity was solving Sudoku puzzles, and you can read more about this here:

This lead to the next step, researching more about exact cover problems, and how they can be solved with well established algorithms:

After building an implementation of Donald Knuth’s Algorithm X, I packaged it up as an AWS Lambda, and then built a React frontend for it:

I have one other project in progress for the frontend, replacing Flux with Redux. I’m still working on that one.

After building my solver and the frontend, I starting thinking about how to generate new puzzles. This is in progress here: https://github.com/kevinhooke/sudoku-generator

If you’re generating new puzzles you also need a way to grade the difficulty of puzzles. The unusual thing about this is there’s no established algorithm for grading the complexity of Sudoku puzzles, they’re typically graded by applying human solving techniques, so this led to this: https://github.com/kevinhooke/sudoku-human-grader

This string of related projects has kept me busy of the past couple of years. Not every idea will lead to a series of related projects like this, but if you can find an idea that does, it will keep you busy with plenty of problems to solve and many learning opportunities.

React Redux cheatsheet

This is my summary of the bare minimum steps as a quick-ref cheatsheet for adding Redux to an existing React app. This is just some notes for future reference. If you’re looking for tutorials, take a look at:

Here’s my quickref:

npm install --save react-redux

Add Redux devtools for Chrome (see here):

npm install --save-dev redux-devtools-extension

Wrap <Provider> around root Component to share the Store with all child components:

import { Provider } from 'react-redux'
import store from './store'

<Provider store={store}>
  <App>
</Provider>

Implement mapStateToProps and mapDispatchToProps to pass store and dispatch() as props to each Component, and call connect() to connect each Component with your Redux Store:

const mapStateToProps = (state) => {
  return {
    yourcomponent: state.yourcomponent
  }
}
export default connect(
  mapStateToProps,
  mapDispatchToProps
)(YourComponent)

Create a Store:

import { createStore } from 'redux';
import { devToolsEnhancer } from 'redux-devtools-extension';
let store = createStore(yourReducer, devToolsEnhancer());
export default store;

Create reducers for Store – here’s an example that take the the value of a label from the passed action payload and sets it in the Store – remember the state of the store is always treated as immutable, so always create a new copy of the modified state, here using Object.assign():

function labelReducer(state = initialState, action) {
  switch (action.type) {
    case 'CHANGE_LABEL':
      return Object.assign( {},{ labelValue : action.payload 
    } );
    default:
      return state;
    }
}

Implement an action creator that builds an Action object that requests changes to the Store – each action has a type and a payload:

export function changeLabel(payload) {
  return { type: "LABLE_CHANGE", payload }
};

Implement mapDispatchToProps – you can either build the Action object yourself here, or call an Action creator like the above example:

function mapDispatchToProps(dispatch) {
return {
  changeLabel: labelValue => 
    dispatch(changeLabel(labelValue))
  };
}

Pass mapDispatchToProps to your connect() call shown earlier.

As a comparison, here’s a simple React app using Flux:

https://github.com/kevinhooke/SimpleReactFluxExample

… and here’s the same app converted to use Redux:

https://github.com/kevinhooke/SimpleReactReduxExample

Done!