Cannot create Store in redux with thunk middlewear - reactjs

I'm having issues with creating my store using redux. I have a feeling it is how my createStore is passing in the middleware.
The line return createStore(rootReducer, applyMiddleware(thunk), initialState)' is giving me the warning Argument Type GenericStoreEnhancer is not assignable to parameter type StoreEnhancer
What does that mean?
I believe I'm following the documentation here correctly, so I'm at a loss.
I know my store is not being created/exported correctly because I'm getting an undefined for my state.
Any ideas how to create the store correctly?
Thanks,
Here is my configureStore.js file:
import {createStore, applyMiddleware} from 'redux';
import thunk from 'redux-thunk';
import rootReducer from '../reducers';
export default function configureStore(initialState) {
return createStore(rootReducer, applyMiddleware(thunk), initialState);
}

You should specify your initial state in each reducer and not by createStore.
For example,
const store = createStore(
rootReducer,
applyMiddleware(thunk)
);
docs:
https://github.com/gaearon/redux-thunk

return createStore(rootReducer, applyMiddleware(thunk), initialState)' is giving me the warning Argument Type GenericStoreEnhancer is not assignable to parameter type StoreEnhancer
Its because ,you are passing parameter in wrong order to creatStore
The right signature is,
createStore(reducer, [preloadedState], [enhancer])
Please refer redux-docs
So,it should be like this.
createStore(rootReducer, initialState, applyMiddleware(thunk))

Related

what is the difference between Redux CombineReducers and Redux-toolkit ConfigureStore

import { configureStore } from "#reduxjs/toolkit";
import testSlice from "./testSlice";
import {combineReducers} from "redux";
const rootReducer = combineReducers({test: testSlice})
export const store = configureStore({
reducer: rootReducer,
});
Which one is better? for performance and use purpose. Which is good to use?
They are totally different things.
The reducer option is an object of slice reducers, like {users : usersReducer, posts : postsReducer}, configureStore will automatically create the root reducer by passing this object to the Redux combineReducers utility. See here
RTK configureStore setup the redux store configuration, not only reducer, but also middlewares, dev tools, preloaded state, and enhancers.
The Redux combineReducers helper function turns an object whose values are different reducing functions into a single reducing function

Getting the root reducer from configureStore redux toolkit

import { configureStore } from "#reduxjs/toolkit";
import testSlice from "./testSlice";
import {combineReducers} from "redux";
const rootReducer = combineReducers({test: testSlice})
export const store = configureStore({
reducer: rootReducer,
});
export type RootState = ReturnType<typeof rootReducer>
This is my current workaround in order to get the type of my root reducer. I'm reading the docs and I can't seem to find how to get my root reducer from the store if I was using slices to create the reducer in my configure store. It's just disappointing that I have to use combineReducers again while using redux-toolkit just to get my rootReducer type.
I'm looking for a code like this:
import { configureStore } from "#reduxjs/toolkit";
import testSlice from "./testSlice";
import userSlice from "./userSlice";
export const store = configureStore({
reducer: {test: testSlice, user: userSlice},
});
export type RootState = ReturnType<typeof store.getReducer()>
A better type for RootState would be as below:
export type RootState = ReturnType<typeof store.getState>
This implementation is included in the document. Please take time to read it.
If your intention of getting the rootReducer isn't only for defining the RootState type, you can simply named-export it from store file and then import it in your desired file to use.
// store.js
// ...
export const rootReducer = combineReducers({test: testSlice});
It's not a common and practical usage for the rootReducer to be imported and consumed in other places but the store file, where it will be used to configure the store object.
Therefore, I think there's no point for Redux Toolkit to provide a way to access the rootReducer.
You don't need to
Huan's answer is spot-on. It is very unlikely that you will actually need to access the root reducer type. You can get the state type without it. Their answer is the best solution to underlying problem.
But if you want to...
If anyone reading this question would like to know how to derive the root reducer type from the store type, it can be done by using TypeScript conditional types. This is the same sort of logic that you see in the redux source code.
We can define a reusable utility type that gets the reducer type from the type of the store. It does this by accessing the types for the state S and the action A, which are both generic type parameters of the redux Store<S, A> type. Then we apply those same generics to the Reducer<S, A> type.
// Can be imported from 'redux' or from '#reduxjs/toolkit', both are the same.
import { Reducer, Store } from '#reduxjs/toolkit';
type ReducerFromStore<T> = T extends Store<infer S, infer A> ? Reducer<S, A> : never
You can use this utility type to get the root reducer type from a specific store instance.
export const store = configureStore({
reducer: {test: testSlice, user: userSlice},
});
export type RootReducer = ReducerFromStore<typeof store>;
Note: The exact type of the store object created by configureStore is the redux toolkit EnhancedStore type. But this extends the core redux Store type, so no special handling is needed.

redux store Error while creating store after installing redux-saga

I want to use redux-saga in my project and after installing redux-saga when I make changes in store.js file it gives error
Error: It looks like you are passing several store enhancers to createStore(). This is not supported. Instead, compose them together to a single function.
# store.js
import { createStore, applyMiddleware, compose } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import rootReducer from './reducers'
import createSagaMiddleware from 'redux-saga';
import rootSaga from './actions/sagas';
const sagaMiddleware = createSagaMiddleware();
const composeEnhancers =
typeof window === 'object' &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
// Specify extension’s options like name, actionsBlacklist, actionsCreators, serialize...
}) : compose;
const enhancer = composeEnhancers(
applyMiddleware(sagaMiddleware)
);
const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
enhancer,
composeWithDevTools(applyMiddleware(...middleware))
);
sagaMiddleware.run(rootSaga);
export default store;
I don't know much about stores. please see if you can help.
You are composing your devtools and middlewares twice there use only one of the two.
const store = createStore(
rootReducer,
initialState,
// either this line
enhancer,
// or this line
composeWithDevTools(applyMiddleware(...middleware))
);
Also, just FYI: this is a quite outdated style of redux. If you are learning redux right now and follow this approach, you will write a LOT of unneccessary boilerplate. Please follow the official redux tutorials over at the official redux documentation instead of whatever tutorials you are learning from right now.

React-Redux, LocalStorage: TypeScript error while saving data lcoally

I am learning React-Redux and TypeScript.
In my small app I want to use localStorage to save data locally.
I tried to solve it according to this answer, but encountered a typescript error. I tried to define type any as a temporary solution, but it didn't help.
Argument of type 'string | null' is not assignable to parameter of type 'string'.
Type 'null' is not assignable to type 'string'
Since there are 2 actions, reducers, I had to use combineReducers. For side effects I am using thunk middleware.
I assume store is the correct component to save data.
Any help is appreciated
rootReducer.ts
import { combineReducers } from 'redux'
import carReducer from './Car/CarReducer'
import ProductReducer from "./Products/ProductReducer"
const rootReducer = combineReducers({
car: carReducer,
products: ProductReducer
})
export default rootReducer
store.ts
import { createStore, applyMiddleware } from 'redux'
import { composeWithDevTools } from 'redux-devtools-extension'
import thunk from 'redux-thunk'
import RootReducer from './RootReducer'
const persistedState = localStorage.getItem('state') ?
JSON.parse(localStorage.getItem('state')) : {} // <--- typescript error here
const store = createStore(
RootReducer,
persistedState,
composeWithDevTools(applyMiddleware(thunk))
)
store.subscribe(()=>{
localStorage.setItem('reduxState', JSON.stringify(store.getState()))
})
export default store
Typescript doesn't have a way to know that calling getItem multiple times is going to result in the same thing being returned each time. So while you do check for null with the first value that's returned, this makes no difference to the second value.
The fix is to just call it once, and save the result to a variable:
const temp = localStorage.getItem('state');
const persistedState = temp ? JSON.parse(temp) : {};

Configuration store variation with React/redux and thunkMiddleware

I've been through many examples to understand how Redux-Thunk works and most the time the store is configured in a various way. I guess there's the old way or the new way but I'm kind of lost. Here is three pattern that I've identified. If someone can explain me the differences between them :
The simple way :
import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk'
import createLogger from 'redux-logger'
const loggerMiddleware = createLogger()
const store = createStore(rootReducer, applyMiddleware( thunkMiddleware, loggerMiddleware));
the Official Reddit Async Exemple way (here) :
import { createStore, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk'
import createLogger from 'redux-logger'
const loggerMiddleware = createLogger()
export default function configureStore(preloadedState) {
return createStore(
rootReducer,
preloadedState,
applyMiddleware(
thunkMiddleware,
loggerMiddleware
)
)
}
the old way ?
import {compose, createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
const createAppStore = compose(
applyMiddleware(thunkMiddleware)
)(createStore);
export default function configureStore(initialState){
const store = createAppStore(rootReducer, initialState);
return store;
};
From there I have at least four questions :
Do we still need to use compose ? I only find it in "old" exemple ?
Is there any differences between import ReduxThunk from
'redux-thunk' and import thunkMiddleware from 'redux-thunk' ?
Does the simple way is also correct ?
I don't understand the preloadedState pattern from the Reddit Async Exemple.
thanks.
The signature for createStore is createStore(reducer, [preloadedState], [enhancer]).
preloadedState is an initial state you load before you initialize your app. For instance, if you prerender your first page on a server and want to pass app state as a JSON inside your HTML. Sometimes you need to fetch this state asynchronously that is where the second example is useful. You fetch your state and create a store using that state in the callback of your ajax call.
The third argument.
enhancer is a higher-order function that composes a store creator to return a new, enhanced store creator. applyMiddleware is a store enhancer shipped with redux. If you want to combine multiple store enhancers you need to use compose function.
For instance, redux-devtools-extension for chrome is an enhancer so to use it in your app you would need compose function
let store = createStore(reducer, initialState, compose(
applyMiddleware(...middleware),
window.devToolsExtension ? window.devToolsExtension() : f => f
));
When you import something from 'redux-thunk' you use default export so you can name your variable as you want. It doesn't matter.
A simple way is also correct if you don't need anything fancy it would work just fine.

Resources