Gatsby: Neither selector or redux gets updated inside component

Created on 1 Apr 2020  路  7Comments  路  Source: gatsbyjs/gatsby

Summary

I don't know if this is a bug or a question. I have an event listener with a selector in the Redux wrapper, this works fine.

ReduxWrapper.js

import React from 'react';
import { Provider } from 'react-redux';
import { createStore as reduxCreateStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk'
import { logger } from 'redux-logger'

import rootReducer from '.';
import { screenResize } from '../actions/uiActions'

const createStore = () => process.env.NODE_ENV === 'development' ? reduxCreateStore(rootReducer, applyMiddleware(thunk, logger)) : reduxCreateStore(rootReducer, applyMiddleware(thunk));

window.addEventListener('resize', () => {
  createStore().dispatch(screenResize())
})


export default ({ element }) => (
  <Provider store={createStore()}>{element}</Provider>
);

The reducer updates properly also

uiReducer.js

const uiReducer = (state = initialState, action) => {
  switch (action.type) {
    case types.SCREEN_RESIZE:
      return { ...state, screenHeight: action.screenHeight, screenWidth: action.screenWidth };

    default:
      return state
  }
}

Then the component is something like

const ComponentName = ({ screenWidth, isMobile }) => {
   return `Screen Width is: ${screenWidth}`;
}

ComponentName.propTypes = {
  screenWidth: PropTypes.number,
  isMobile: PropTypes.bool
}

const mapStateToProps = (state) => {
  return {
    isMobile: isMobile(state),
    screenWidth: state.ui.screenWidth
  }
}

export default connect(mapStateToProps)(ComponentName)

I also tried to convert to class component, use getDerivedStateFromProps, and it still does not update, neither the screenWidth, nor the isMobile.

If dispatch actions from the component, like I am doing somewhere else, it works, this that gets dispatched from outside the component does not work

Relevant information

Environment (if relevant)

System:
OS: Windows 7 6.1.7601
CPU: (4) x64 AMD A6-3670 APU with Radeon(tm) HD Graphics
Binaries:
Node: 13.7.0 - C:Program Filesnodejsnode.EXE
Yarn: 1.2.1 - C:Program Files (x86)Yarnbinyarn.CMD
npm: 6.13.4 - C:Program Filesnodejsnpm.CMD
Languages:
Python: 2.7.17 - /c/Python27/python
npmPackages:
gatsby: ^2.19.7 => 2.19.21
gatsby-background-image: ^0.10.2 => 0.10.2
gatsby-image: ^2.2.39 => 2.2.41
gatsby-plugin-apollo: ^3.0.1 => 3.0.1
gatsby-plugin-manifest: ^2.2.39 => 2.2.42
gatsby-plugin-offline: ^3.0.32 => 3.0.35
gatsby-plugin-postcss: ^2.1.20 => 2.1.20
gatsby-plugin-react-helmet: ^3.1.21 => 3.1.22
gatsby-plugin-robots-txt: ^1.5.0 => 1.5.0
gatsby-plugin-sharp: ^2.4.7 => 2.4.7
gatsby-plugin-stylus: ^2.1.21 => 2.1.21
gatsby-source-filesystem: ^2.1.46 => 2.1.48
gatsby-transformer-sharp: ^2.3.17 => 2.3.17

File contents (if changed)

gatsby-config.js: N/A
package.json: N/A
gatsby-node.js: N/A
gatsby-browser.js:

export { default as wrapRootElement } from './src/state/ReduxWrapper';


gatsby-ssr.js:

export { default as wrapRootElement } from './src/state/ReduxWrapper';

question or discussion

Most helpful comment

I assumed gatsby-browser only here, because of window.addEventListener which would fail in ssr (window is not defined), but yeah - redux store should probably be recreated for each page (it somewhat depends on the state shape and actions you dispatch on initial render - you might not to re-init store for each page).

In any case, I do think ResizeWatcher is the way to go here :)

All 7 comments

@algebrathefish can you make a reproduction following these steps with your code and packages so that it can be better looked at? I won't mind taking a look and provide you with possibly a solution and a more detailed answer.

Sounds good?

@jonniebigodes yes, thank you! it sounds great. It will until tomorrow to do it, I will let you know.

I appreciate your help

@jonniebigodes here is my reproduction https://github.com/algebrathefish/gatsby-redux-test

(I started from my own project instead of from the gatsby starter so as not to have to configure everything again, is that ok)

I was proposed this solution https://spectrum.chat/gatsby-js/general/selector-and-reducer-data-not-updating-in-props-in-the-component~c9824761-5569-45a4-bad6-46538e310136?m=MTU4NTc2MzM0MTUxMw== so I am going to try that

If you have another idea, please let me know

Thank you

As @Js-Brecht mentioned in the spectrum - this is because you dispatch action to different store than one you are listening to

Alternative to changes there would be to do:

import React from 'react';
import { Provider } from 'react-redux';
import { createStore as reduxCreateStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk'
import { logger } from 'redux-logger'

import rootReducer from '.';
import { screenResize } from '../actions/uiActions'

-const createStore = () => process.env.NODE_ENV === 'development' ? reduxCreateStore(rootReducer, applyMiddleware(thunk, logger)) : reduxCreateStore(rootReducer, applyMiddleware(thunk));
+const store = process.env.NODE_ENV === 'development' ? reduxCreateStore(rootReducer, applyMiddleware(thunk, logger)) : reduxCreateStore(rootReducer, applyMiddleware(thunk));

window.addEventListener('resize', () => {
-  createStore().dispatch(screenResize())
+ store.dispatch(screenResize())
})


export default ({ element }) => (
-  <Provider store={createStore()}>{element}</Provider>
+  <Provider store={store}>{element}</Provider>
);

That would ensure dispatching to same store.

But I think Jeremy's solution with ResizeWatcher component is generally cleaner/better, because it encapsulates viewport size watching nicely and you don't mix redux store setup and resize watching in same file (always hard to untangle that later on)

IIRC, @algebrathefish is using that ReduxWrapper.js default export in both gatsby-ssr and gatsby-browser. In the case of your alternative, @pieh, I think he would also need to make sure he creates a new store on every call during SSR, yeah?

I assumed gatsby-browser only here, because of window.addEventListener which would fail in ssr (window is not defined), but yeah - redux store should probably be recreated for each page (it somewhat depends on the state shape and actions you dispatch on initial render - you might not to re-init store for each page).

In any case, I do think ResizeWatcher is the way to go here :)

@algebrathefish you indicated on spectrum that the problem is solved. I think we can close this issue now 馃檪

If you have any more questions, don't hesitate to ask!

Was this page helpful?
0 / 5 - 0 ratings