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
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
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';
@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!
Most helpful comment
I assumed
gatsby-browseronly here, because ofwindow.addEventListenerwhich 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
ResizeWatcheris the way to go here :)