I have an Electron desktop application containing a React + Redux app. Redux works fine whilst running the app locally in development, but does not update components once the app has been built to a native desktop app.
Many articles and issues seem to think that the store is configured incorrectly, or that I am mutating state directly rather than updating it, or that my mapStateToProps function is not set up correctly.
I can't see how any of these can be an issue, as my app works during development, and I can see the store be updated and the component re-renders.
In production though, the following happen:
The store state is initialized
An action is dispatched
The state updates ( I can see in the dev tools )
None of my connected components re-render with updated props.
The only difference I can see between development and production is that within Electron, during dev the app is loaded via http://localhost, and during production it is loaded via file:///
My store is configured as:
export const store = createStore(rootReducer, composeWithDevTools(applyMiddleware(thunk)));
The store is provided as:
<Provider store={store}>
<App />
</Provider>
I am connecting my component to the store as:
const mapStateToProps = (state: RootState) => {
return {
hasLoaded: state.products.hasLoaded,
products: state.products.products
};
};
const mapDispatchToProps = {
getProducts: getProducts
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(ProductsList);
Then calling getProducts from within the component like:
componentDidMount() {
this.props.getProducts();
}
I expect the state to be updated with the retrieved products, and then the component should re-render with the updated store state as props.
Instead, the store state is updated - as I can see it in Redux DevTools - but the connected component never re-renders, nor does componentDidUpdate get called.
I spent many days with the same feeling and issue that you did. After detecting that in production the subscribe function from redux still worked, but react-redux was unable to connect properly, I tried downgrading and it worked.
Can you try just removing composeWithDevTools?
export const store = createStore(rootReducer, applyMiddleware(thunk));
The example below uses the React application store. It also doesn't work with Redux devtools.
const store = createStore(
rootReducer,
initialState,
compose(
applyMiddleware(...middleware),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
);
I removed this part before production build because it required a browser dev plugin; I hope React Native will be the same.
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
Adding the following to electron-webpack.config.json solved the issue, react-redux has to be whitelisted.
{
"whiteListedModules": ["react-redux"]
}
Related
I am using redux, react-redux and redux thunk for store management in my react native project, there is deferent reducers and actions. How I reset the entire store during logout
Is there any methods like createStore() to destroy a store?
please share possible solutions
Thanks
The below solution worked for me,
import reduxReset from 'redux-reset'
const enHanceCreateStore = compose(
applyMiddleware(thunk),
reduxReset()
)(createStore)
const store = enHanceCreateStore(reducers)
dispatch reset action
store.dispatch({
type: 'RESET'
})
We have a NextJS application using next-redux-wrapper and Redux thunks. We have a page that works fine when we load the page via a local link, that is, it's rendered locally, but when we reload the page, thus rendering it on the server, our store remains (partially) empty: certain fields are never filled.
I'm using Redux dev tools to follow the actions, but all I ever see when I reload the page in the list of actions is ##init. When I put log statements in I see -- in the server-side console -- that my reducer is being called with valid values. However that field remains empty in the store, as shown by RDT in the browser.
The Redux actions are not showing up in the browser Redux Dev Tools console because they are happening on the server.
The store is set up as next-redux-wrapper instructs
// _app.ts
import withRedux from 'next-redux-wrapper';
import initStore from '../redux/store';
const makeStore = initialState => {
return initStore(initialState);
};
const MyApp = ({ Component, pageProps, apollo, store }: Props) => {
return (
<ApolloProvider client={apollo}>
<Provider store={store}>
<Sidebar />
<Component {...pageProps} />
</Provider>
</ApolloProvider>
);
};
MyApp.getInitialProps = async appContext => {
const { Component, ctx } = appContext;
const appProps = await App.getInitialProps(appContext);
const pageProps = Component.getInitialProps
? await Component.getInitialProps(ctx)
: {};
const allProps = {
...appProps,
...pageProps
};
return { ...allProps };
};
export default withRedux(makeStore)(withApollo(MyApp));
How can I figure out what's happening in my Redux store if I can't use Redux Dev Tools to see? What I'd like to do is find out exactly where and when the values that are being passed to the reducers are overwritten with a blank value.
The answer turned out to be that I was dispatching a thunk within my thunk server side, and I suppose that the result didn't come back in time to make it to the store transfer from NextJS server to the client. When I made it a direct, async, call within my thunk, all worked fine.
I am developing my project using React and Redux. I am getting below error when I run my project after few days later. Could anyone say why I am getting below error.
This is how I configure redux store:
const enhander = compose(applyMiddleware(...featureMiddleware, ...coreMiddleware, logger, thunk));
export const store = createStore(rootReducer, {}, enhander);
The only difference is I provide an initial state which is an empty object
Greetings.
I am trying to write a react native application making use of both redux and react native navigation. However, I do not actually want to save my navigation state in redux. So I just want to keep them separated from each other. I currently have the StackNavigator and that is wrapped in a connect which then is wrapped by the provider.
const mapStateToProps = state => state;
const mapDispatchToProps = dispatch => dispatch;
// need this Higher Order Component so you can pass properties through the root stack
const AppContainer = connect(mapStateToProps, mapDispatchToProps)(RootStack);
export default class App extends Component {
render() {
return (
<Provider store={Store}>
<AppContainer />
</Provider>
);
}
}
Is this implementation possible?
Yes, this is possible, and in fact, encouraged.
Quotation from react-navigation docs:
Some folks like to have their navigation state stored in the same place as the rest of their application state. Think twice before you consider doing this, there is an incredibly good chance that you do not need to do this!. Storing your React Navigation state in your own Redux store is likely to give you a very difficult time if you don't know what you're doing.
By default, Redux and React-Navigation have nothing to do with each other, and in its next release (Fall 2018) support for integrating React-Navigation into your redux state will not be documented or encouraged at all.
I am using react-redux and I am having a issue where I loose my redux state when the page is refreshed.
Now before I go further on that this is the scenario, which I may be messing up.
Question one: Can I connect to more than one layout?
I have a dashboard and a "app" layout. Both have separate layouts. I connect both in the same manner:
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as actionCreators from '../actions/actionCreators';
function mapStateToProps(state) {
return {
profile: state.profile,
child: state.child,
}
}
function mapDispachToProps(dispatch) {
return bindActionCreators(actionCreators, dispatch);
}
const LayoutApp = connect(mapStateToProps, mapDispachToProps) (DashboardLayout);
export default LayoutApp;
The dashboard connects just fine. I am able to hit reducers when i need to and update the store, however the dashboard links to the app for certain parts of data manipulation for you to play with. When it links I get the data in props as expected, however as soon as any page refreshes within the app layouts I loose the props being sent by maptoprops.
I have tried to combine into one master layout however that seems to have the same affect. I have also tried to save to state immediately when i first receive data but that seems to be lost as well upon refresh which makes me think it is resetting it.
Summary:
- DashboardLayout (connects to redux)
- AppLayout (connects to redux) however after a page refresh it looses props to the Applayout and needed data is gone.
Get to know redux-persist
https://github.com/rt2zz/redux-persist
You can install it using
npm i --save redux-persist
persistStore is the function that allows you to persist store.
import {persistStore} from 'redux-persist'
and autoRehydrate is the action that is performed whenever the state needs to be rehydrated
import {autoRehydrate} from 'redux-persist'
following is the structure that may be useful.
import {compose, applyMiddleware, createStore} from 'redux'
import {persistStore, autoRehydrate} from 'redux-persist'
// add `autoRehydrate` as an enhancer to your store (note: `autoRehydrate` is not a middleware)
const store = createStore(
reducer,
undefined,
compose(
applyMiddleware(...),
autoRehydrate()
)
)
// begin periodically persisting the store
persistStore(store)
and then for your reducer
import {REHYDRATE} from 'redux-persist/constants'
//...
case REHYDRATE:
var incoming = action.payload.myReducer
if (incoming) return {...state, ...incoming, specialKey:
processSpecial(incoming.specialKey)}
return state
following are the methods which can be used to work out of redux-persist
persistor.pause() - pauses redux persist
persistor.resume() - resumes redux persist
persistor.purge() - deletes all persisted data
persistor.rehydrate() - calls reducer to rehydrate store
types of storage redux persist allows
// sessionStorage
import { persistStore } from 'redux-persist'
import { asyncSessionStorage } from 'redux-persist/storages'
persistStore(store, {storage: asyncSessionStorage})
// react-native
import {AsyncStorage} from 'react-native'
persistStore(store, {storage: AsyncStorage})
// web with recommended localForage
import localForage from 'localforage'
persistStore(store, {storage: localForage})
it is the most basic use redux-persist hope it helps.
You can connect to as many components as you want to same store properties - that's not issue. Just to note here, connect is listening for changes so when action changes store it would cause all components listening to changed store parts to re-render (or at least do computation and then compare shadowdom).
Store is stored only when running application - after page close/resfresh it's cleared.
There is good thread about persistence of store (eg. to local storage): Where to write to localStorage in a Redux app?
As mentioned in the previous answers storing the state in local storage is one possible solution however some other possible solutions are
Make sure your links use the Push API ie <Link to={"route"}/> (react-router) this will ensure the store is not removed between route changes
Set your initial state when you create your store
const rootReducer = combineReducers({
todos: todos,
visibilityFilter: visibilityFilter
});
const initialState = {
todos: [{id:123, text:'hello', completed: false}]
};
const store = createStore(
rootReducer,
initialState
);
This will hydrate your reduce store when it is initialized
There are some other libs/frameworks/boilerplates that you could use that also implement server rendering with will render the HTML to the page on the initial render and then load React once the DOM has loaded some of this libs are
Next.js,Electroide,react-redux-universal-hot-example
Going down this route will add significant complexity to your app so you should weigh up the pros and cons before embarking down this route
I had this problem, so as somebody mentioned here, i used the localStorage.
In my reducer, i did like this:
const exampleKey = "_exampleKey"
const INITIAL_STATE = {
example: JSON.parse(localStorage.getItem(exampleKey))
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case 'SET_TRUE':
localStorage.setItem( exampleKey, JSON.stringify(true));
return { ...state, example: true };
case 'SET_FALSE':
localStorage.setItem( exampleKey, JSON.stringify(false));
return { ...state, example: false};
default:
return state
}
Pointing INITIAL_VALUES.example to localStorage's item example ensure us to keep the correct value when the page reloads.
Hope it helps somebody.