Redux devtools stopped working: upgrading dexuv-devtools-extension

The Redux Devtools Chrome plugin stopped working for me, possibly after a Chrome update. I removed and re-installed the plugin and it still wasn’t working. It was opening up ok but not picking up any of my Redux actions or state changes.

Checking the docs, it seems there’s been a name change with how you integrate the devtools into you app too. Since I’m adding the thunk middleware I need to use the compose approach. Before updating and before this naming change, I was using this approach, described in a previous post here:

import { devToolsEnhancer } from 'redux-devtools-extension';
import rootReducer from '../reducers/reducer';

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

New approach with updates react-devtools and the extension:

import { composeWithDevTools } from 'redux-devtools-extension';
import rootReducer from '../reducers/reducer';

const store = createStore(
    rootReducer,
    composeWithDevTools(
      applyMiddleware(thunk)
      // other store enhancers if any
    )
  );

React app running locally failing with opensslErrorStack ERR_OSSL_EVP_UNSUPPORTED

I just started getting this error starting up my React app locally with ‘npm run start’ :

opensslErrorStack: [ 'error:03000086:digital envelope routines::initialization error' ],
  library: 'digital envelope routines',
  reason: 'unsupported',
  code: 'ERR_OSSL_EVP_UNSUPPORTED'

I recently installed nvm on my Mac to avoid needing sudo for running a ‘npm i -g’, and also apparently picked up the latest stable node version and not lts.

Running ‘nvm ls’ to see what version I have and what’s available:

> nvm ls
->      v17.3.0
         system
default -> node (-> v17.3.0)

According to https://nodejs.org/en/download/current/ 17.3.0 is the latest current and not lts. Answers to this related question suggested switching back to lts to avoid this change.

Following the nvm docs :

nvm install -lts
nvm use --lts

Now I’ve back at version 16.13.1, and my React app with ‘npm run start’ now starts as expected.

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.