I've had it like this before:
createStore(reducers, {}, applyMiddleware(reduxThunk));
Just saw a different piece of code somewhere and now changed my code to this, using compose:
createStore(reducers, {}, compose(applyMiddleware(reduxThunk)))
Both work fine so far. But I don't understand the difference exactly. Was what I had before wrong? Could someone explain this to me?
A "store enhancer" is a specific type of Redux addon that wraps around a store to give it extra abilities.
createStore accepts a single store enhancer as an argument, and uses that to customize the created store.
That means that if you want to use multiple store enhancers at once, you need a way to somehow combine them into a single store enhancer so you can pass it to createStore.
applyMiddleware is a store enhancer, so you can use it directly and pass its result as the one-and-only enhancer to createStore.
If you want to use multiple enhancers, like both applyMiddleware and devTools, you need to use compose() to combine them together.
Note that you should really be using our official Redux Toolkit package, which has a configureStore API that will already set up a Redux store correctly for you with one line of code.
#markerikson is right. Both of them work well.
If you are working on simple React app, the first one is enough.
If you handle large projects, you need to use the second one. Like below:
const history = createHistory();
const sagaMiddleware = createSagaMiddleware();
const routeMiddleware = routerMiddleware(history);
const middlewares = [thunk, sagaMiddleware, routeMiddleware];
const composeEnhancers =
(window['REDUX_DEVTOOLS_EXTENSION_COMPOSE'] as typeof compose) || compose;
const store = createStore(
combineReducers({
...reducers,
router: connectRouter(history),
}),
composeEnhancers(applyMiddleware(...middlewares)),
);
From redux docs:
Tips
All compose does is let you write deeply nested function transformations without the rightward drift of the code. Don't give it too much credit!
Also you can add some enhancements differ from thunk and middlewares. Check out this comment
Related
Can you please explain what is the reason to use useStore() function in vue 3 component (composition-api)?
I'm confused, because the direct import of the store is also works, e.g.:
<script setup>
import { store } from '#/store';
const foo = computed(() => store.getters['foo']); // works!
</script>
But a lot of the time I see people are using useStore() instead:
<script setup>
import { useStore } from 'vuex';
const store = useStore();
const foo = computed(() => store.getters['foo']); // also works well
</script>
Why? So far feels just as an extra line of code.
I assume I'm missing something.
Thank you
Important update:
I found out that useStore() provides value for unit testing. If you are not using useStore() it's might be not possible to mock the store.
It is all about the newest store instance in composition API, as per docs:
We talk about how to retrieve the store in Vue 2 & Vuex 3 Project. Maybe you already have the answer, it's very easy, just use this.$store.
But, We know about Composition API, Inside setup() this won't be a
reference to the current active instance Since setup() is called
before other component options are resolved.
documentation
In my CRA project, I have a configureStore.ts file that exports my redux store:
// ...
export const store = createStore(
rootReducer,
initialState,
compose(applyMiddleware(...middleware), ...enhancers)
);
and I subscribe to the store manually in staticIntl.ts, which is imported hundreds of times throughout the whole project
import { store } from "../configureStore"
class IntlManager {
constructor() {
store.subscribe(this.onStoreChange);
}
// ...
}
const manager = new IntlManager()
But when I run the project, the store.subscribe fails due to
TypeError: Cannot read property 'subscribe' of undefined
I am practically certain this is due to import order/availability
I managed to "fix" the issue by pushing the subscribe callback to the end of the task queue via no-delay setTimeout (I know it actually rounds up to couple of MS, but I don't think I really need to mind about that):
constructor() {
setTimeout(() => store.subscribe(this.onStoreChange));
}
Is this safe? I am mostly concerned about three things:
is it possible for the subscribe to still fail due to the same reason, even when wrapped in setTimeout ? For example if somehow processing my modules takes a long time
Is it possible for a first store update to happen before subscribtion (and thus missing on the subscribe callback call)?
Could such update happen as a result of my React app fetching data from the server and dispatching the response?
Are there any other potential pitfalls of my fix/hack?
Also, what other ways could I fix my problem? I'd be especially thankful for a link to a propper guide.
I practically certain this is due to import order
Yes, this sounds very much like a problem with circular dependencies. Your configureStore imports something that depends on staticIntl, which in turn imports the store from a module that is not yet evaluated.
Do not use setTimeout, fix the underlying problem with your module dependencies.
In particular, I would recommend
move const manager = new IntlManager() into a different file where you clearly control that it is executed after the createStore(…) call.
Use dependency inversion - remove the store.subscribe(this.onStoreChange) line from the IntlManager constructor and instead write store.subscribe(manager.onStoreChange) after you created store.
I'm assuming IntlManager is a singleton, and you only need one of these in your app - if this is not true, ignore the following.
Following the classic redux setup https://redux.js.org/recipes/configuring-your-store my advice is to pass the store as a prop to your main App component. And then create an instance of IntlManager by passing the store prop to the constructor:
const store = configureStore()
render(
<Provider store={store}>
<App store={store} />
</Provider>,
document.getElementById('root')
)
in App component:
const { store } = this.props;
const manager = new IntlManager(store);
Wether this works depends a bit on how IntlManager is actually used by your app. You wrote that it is "imported hundreds of times throughout the whole project". This is ok I think, you might have to rewrite the class a bit so that you can instantiate it right away, but subscribe to the store later, when the store is actually available. I envision something like this:
in App component:
import manager from '.../IntlManager';
// somewhere in componentDidMount for example:
const { store } = this.props;
manager.subscribeToStore(store);
I am trying to write a React app and I am trying to use ConnectedRouter:
https://github.com/supasate/connected-react-router
It's a Redux binding for React Router.
I am getting the following error:
Now I think this is probably related to this question's accepted answer:
Redux: Unexpected key found in preloadedState argument passed to createStore
However unlike there when trying to pass default, I actually probably want these in my combine reducer.
Here's my current code in my reducers/index.js:
export default history =>
combineReducers({
router: connectRouter(history),
search,
profile,
color,
categories,
coordinates: LocationReducer,
idprovider,
firstFavorite,
analytics,
sidebar,
messages,
total_messages,
onesignal,
tokens
});
And in my store.js:
import createRootReducer from "./reducers/index";
I'm not quite sure what the correct solution is here, as ConnectedRouter doesn't seem to do anything with these values.
What is the correct solution?
EDIT: In my example bellow I used syntax used in connected-react-router v4, but my example was definitely wroking.
There was an update in usage for v5/v6, if you are using version>=5, try to migrate my example into it:
https://github.com/supasate/connected-react-router/blob/master/FAQ.md#how-to-migrate-from-v4-to-v5v6
You probably do not intialize the store correctly.
Try this:
reducers/index.js
export default combineReducers({
// router reducer will be added automatically by connectRouter in store.js
search,
profile,
color,
categories,
coordinates: LocationReducer,
idprovider,
firstFavorite,
analytics,
sidebar,
messages,
total_messages,
onesignal,
tokens
});
store.js
import {connectRouter, routerMiddleware} from 'connected-react-router';
import {createBrowserHistory} from 'history';
import reducers from './reducers';
const history = createBrowserHistory(history);
const store = createStore(
connectRouter(history)(reducers),
applyMiddleware(routerMiddleware(history))
);
I am getting this error:
Unexpected keys "dispatch", "subscribe", "getState", "replaceReducer",
"liftedStore" found in preloadedState argument passed to createStore.
Expected to find one of the known reducer keys instead: "form".
Unexpected keys will be ignored.
My new redux store, with applied middleware is:
// redux extension
const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION__ || compose;
const enhancer = composeEnhancer(applyMiddleware(promiseMiddleware()));
const store = createStore(reducer, enhancer);
I followed the guidelines from here:
https://github.com/zalmoxisus/redux-devtools-extension
Replace __REDUX_DEVTOOLS_EXTENSION__ with __REDUX_DEVTOOLS_EXTENSION_COMPOSE__. The former is a complete enhancer that you can pass directly to createStore in case you're not using any middleware or enhancers. The latter is the composition function you're looking for.
Can anyone help me out with the right redux store configuration?
I am trying to configure as follows,
configureStore.js
import thunk from 'redux-thunk';
import logger from 'redux-logger';
import { createStore, applyMiddleware } from 'redux';
import rootReducer from '../reducers';
let middlewares = [thunk, logger]
const configureStore = () => {
const store = createStore(
rootReducer,
applyMiddleware(...middlewares) // I also tried with applyMiddleware(thunk, logger)
);
return store;
}
export default configureStore;
And I import this module main js file and use as follows,
index.js
import configureStore from '../stores/configureStore';
const store = configureStore();
// And other DOM render code follows...
But is seems not working and the following error displays in the web console,
Error: Uncaught TypeError: WEBPACK_IMPORTED_MODULE_0__compose.a.apply(...) is not a function
File in which the error is:
I am following official docs of redux, redux-thunk and redux-logger but unable to make it work.
Another note: It works fine when I remove any one of thunk and logger from the middlewares list.
UPDATE: I got temporary fix
Downgrading redux from 3.7.2 to 3.5.2 works fine.
I still wonder why it is the problem with latest version of Redux. I tried to add more information related to the issue and looking for solution for future upgrades.
Thanks!
You just made a tiny mistake.
From the official docs, here is the signature of createStore:
createStore(reducer, [preloadedState], [enhancer])
You mixed up the order of the arguments, passing your enhancer function (applyMiddleware) where the function is expecting the preloadedState. You need to pass in an argument in between the rootReducer, and the applyMiddleware function, in order to satisfy the signature.
Hope this helped!