evianpring evianpring - 3 months ago 12
React JSX Question

Why aren't these two versions of a higher order function giving the same results?

Below I am modifying the dispatch function of my Redux store so that it

console.logs
the state of my application (I am aware that this is not the smart way to do this but my question is related to JS syntax not Redux).
There are two versions of the higher order function
addLoggingToDispatch(dispatch)
, and the only difference is that the second version is using arrow functions on all function definitions.

Question: Why aren't these two implementations of addLoggingToDispatch exactly the same? From my understanding of JS and arrow functions they are the same, but I'm not getting the same result in my browser, so I would like to understand why.

Expected behavior: Specifically, what the code below does is that when the "DOMContentLoaded" event happens, I replace the original Redux store.dispatch function with my own addLoggingToDispatch, which just calls the original dispatch(action) function in between logging the state of my application to the console.

Actions are dispatched in my application on
keydown
or
keyup
events. So if the user simply presses a key and releases the key, I should see four messages in the console (the state of the application before and after each of the events).

With the first version of addLoggingToDispatch the logging works as expected. With the second version, when the user presses and releases a key, the logging starts to occur without end, eventually using up all my memory.

import React from 'react';
import ReactDOM from 'react-dom';
import { applyMiddleware } from 'redux';

import configureStore from './store/store';
import Root from './components/root';

//VERSION 1: working version
const addLoggingToDispatch = (store) => {
const dispatch = store.dispatch;
return (action) => {
console.log("Old State: ", store.getState());
console.log("Action: ", action);
dispatch(action)
console.log("New State", store.getState());
}
}

// VERSION 2: does not work
const addLoggingToDispatch = store => action => {
const dispatch = store.dispatch;
console.log("Old State: ", store.getState());
console.log("Action: ", action);
dispatch(action)
console.log("New State", store.getState());
}

document.addEventListener('DOMContentLoaded', () => {
const store = configureStore();
store.dispatch = addLoggingToDispatch(store);
debugger;
const rootEl = document.getElementById('root');
ReactDOM.render(<Root store={store} />, rootEl);
});

Answer

The difference has nothing to do with arrow functions, but only with the creation of the dispatch variable. As @nnnnnn noted in the comments, by not storing the property before overwriting it you are creating an infinite recursion.

However, even the working version looks like a really bad approach. Overwriting store.dispatch and storing the original value should happen in the same place, or even be factored out into one function, instead of being scattered over your code base. This helps to understand what happens here and prevents mistakes such as yours.
Better wrap dispatch in a single line:

const addLogging = (state, fn) => function(x) {
  console.log("Before: ", state());
  console.log("Argument: ", x);
  fn.call(this, x);
  console.log("After: ", state());
};

document.addEventListener('DOMContentLoaded', () => {
  const store = configureStore();
  store.dispatch = addLogging(() => store.getState(), store.dispatch);
  const rootEl = document.getElementById('root');
  ReactDOM.render(<Root store={store} />, rootEl);
});