I have a function that check the role of a user. I want to base on that pass different reducer into a store, is that possible?
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import { getUserRole } from './utils'
import { composeWithDevTools } from 'redux-devtools-extension'
import userReducers from './reducers'
import adminReducers from './reducers/admin'
//share reducer btw member and admin
let reducers
if (getUserRole() === 'member') {
reducers = userReducers
} else {
reducers = adminReducers
}
console.log('reducers', reducers) //undefined unless I refresh the page
const store = createStore(
reducers,
composeWithDevTools(
applyMiddleware(thunk)
)
)
export default store
I found a problem, reducers is undefined for the first time (like I login) unless I refresh it.
Related
Redux newbie here.
I have an reducer ProductListReducer that takes asynchronous action (its action creator fetch data from API), whose initial state is InitialProductListStates. I have another synchronous reducer CartReducer, whose initial state is InitialCartListStates. I want to have them work on one single store store. How should I do this?
My attempt: I want to first combine the two reducers into one reducer with combineReducers, and then combine their initial states as InitialState = {ProductListReducer: InitialProductListStates, CartReducer:InitialCartListStates}. Next, I want to use createStore():
store = createStore(reducer, InitialState , applyMiddleware(thunk));
However, I am stuck at the first test-of-concept: After combining ProductListReducer with itself and setting InitialState={ProductListReducer: InitialProductListStates} , I re-run the app and get this error:
Uncaught Error: Minified Redux error #12; visit https://redux.js.org/Errors?code=12 for the full message or use the non-minified dev environment for full errors.
at redux.js:441:13
at Array.forEach (<anonymous>)
at redux.js:434:25
at _l.ProductListReducer (redux.js:499:5)
at main.ts:4:17
at index.tsx:13:27
at index.tsx:13:27
My codes (that doesn't work):
import reducer from './reducers/main';
import { ProductListReducer, InitialProductListStates } from './reducers/reducers'
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
const InitialStates = {
ProductListReducer: InitialProductListStates
};
export const store = createStore(reducer, InitialStates, applyMiddleware(thunk));
My code (that did work):
import reducer from './reducers/main';
import { ProductListReducer, InitialProductListStates } from './reducers/reducers'
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
const InitialStates = {
ProductListReducer: InitialProductListStates
};
export const store = createStore(ProductListReducer, InitialProductListStates, applyMiddleware(thunk));
I am creating an app which fetches the list of cities entered by a user and their respective weather using openweather API. I want the cities, as a result, to remain after I refresh the page. I know how to persist the data using browser localStorage API. But I don't know how to persist it through a middleware and pass it to the provider.
Below is my main Index.js file-:
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import ReduxPromise from 'redux-promise';
import App from './components/app';
import reducers from './reducers';
import {loadState,saveState} from './localStorage';
const persistedState=loadState();
const store=createStore(persistedState);
store.subscribe(()=>{
store.getState()
});
const createStoreWithMiddleware =
applyMiddleware(ReduxPromise)(createStore);
ReactDOM.render(
<Provider store={createStoreWithMiddleware(reducers)}>
<App />
</Provider>
, document.querySelector('.container-fluid'));
This is the localStorage.js file-:
export const loadState=()=>{
try{
const serializedState=localStorage.getItem('state');
if(serializedState===null){
return undefined;
}
return JSON.parse(serializedState);
}catch(err){
return undefined;
}
};
export const saveState=(state)=>{
try {
const serializedState=JSON.stringify(state);
localStorage.setItem('state',serializedState);
}catch(err){
console.log(err);
}};
I have written the reducer in different files in reducers folder.
reducers/index.js -:
import { combineReducers } from 'redux';
import WeatherReducer from './reducer_weather';
const rootReducer = combineReducers({
weather:WeatherReducer
});
export default rootReducer;
and another reducer in
reducers/reducer_weather.js -:
import {FETCH_WEATHER} from '../actions/index';
import {DELETECITY} from '../actions/deleterow';
export default function(state=[],action){
switch(action.type){
case FETCH_WEATHER:
console.log([action.payload.data, ...state]);
return [action.payload.data, ...state];
case DELETECITY:
console.log([...state].filter((weather)=>{
return weather.city.id!==action.payload
}));
return [...state].filter((weather)=>{
return weather.city.id!==action.payload
});
default:
return state;
};
}
Before using the localStorage for persisting the application state, my code was working fine. But now it is showing me an error. I am facing a problem regarding const createStoreWithMiddleware =
applyMiddleware(ReduxPromise)(createStore);
how to use store using this middleware and how to pass in <Provider> tag's store property?
I would suggest you to use redux-persist.
Persist and rehydrate a redux store
Using this middleware all the data you have in the Redux Store will be automatically persisted to the localStorage (different type of storage can be used too). Therefore, when you refresh a page, the already persisted data from the previous request will be automatically rehydrated (pushed) back to the Store.
Example, taken from documentation:
Configuration:
// configureStore.js
import { createStore } from 'redux'
import { persistStore, persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage' // defaults to localStorage for web and AsyncStorage for react-native
import rootReducer from './reducers'
const persistConfig = {
key: 'root',
storage,
}
const persistedReducer = persistReducer(persistConfig, rootReducer)
export default () => {
let store = createStore(persistedReducer)
let persistor = persistStore(store)
return { store, persistor }
}
Usage:
import { PersistGate } from 'redux-persist/integration/react'
// ... normal setup, create store and persistor, import components etc.
const App = () => {
return (
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<RootComponent />
</PersistGate>
</Provider>
);
};
I'm reading a code of a react project I found on GitHub and I found a different use of redux thunk, by seeing the official documentation at GitHub they use it this way:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers/index';
// Note: this API requires redux#>=3.1.0
const store = createStore(
rootReducer,
applyMiddleware(thunk)
);
but in the code I found the guy used:
import { AsyncStorage } from 'react-native';
import { applyMiddleware, createStore } from 'redux';
import { autoRehydrate, persistStore } from 'redux-persist'
import thunk from 'redux-thunk';
import reducers from '../reducers';
const middleWare = [thunk];
const createStoreWithMiddleware = applyMiddleware(...middleWare)(createStore);
export default configureStore = (onComplete) => {
const store = autoRehydrate()(createStoreWithMiddleware)(reducers);
persistStore(store, { storage: AsyncStorage }, onComplete);
return store;
};
what is the difference between using it as a list or as an object?
Let's look at the function signature (source):
function applyMiddleware(...middlewares) { /* logic */ }
applyMiddleware uses the rest parameter syntax which allows representing an indefinite number of arguments as an array.
Because of this, both
applyMiddleware(thunk, otherMiddleware);
and
const middlewares = [thunk, otherMiddleware];
applyMiddleware(...middlewares);
are equally valid.
I am attempting to implement redux-persist to persist a specific reducer only, invoices, version, but in my case, my reducers are all gone.
This is the entry point:
import React, { Component } from "react";
import { Provider } from "react-redux";
import { createStore, applyMiddleware, compose } from "redux";
import { persistStore, persistCombineReducers } from "redux-persist";
import { createWhitelistFilter } from "redux-persist-transform-filter";
import { PersistGate } from 'redux-persist/es/integration/react';
import storage from "redux-persist/lib/storage";
import thunkMiddleware from "redux-thunk";
import { apiMiddleware } from "redux-api-middleware";
import reducers from "./actions/reducers";
import AppContainer from "./containers/App";
import FlashMessage from "./containers/common/FlashMessage";
import "./App.css"
const middleware = [thunkMiddleware, apiMiddleware];
const persistConfig = {
storage,
key: 'root',
transforms: [
createWhitelistFilter('invoices')
]
};
const store = createStore(
persistCombineReducers(persistConfig, reducers),
compose(
applyMiddleware(...middleware),
)
);
const persistor = persistStore(store);
export default class App extends Component {
render () {
return (
<Provider store={store}>
<PersistGate persistor={persistor}>
{/*<FlashMessage/>*/} // Since there is no reducer, this fails
<AppContainer/>
</PersistGate>
</Provider>
)
}
}
And this is the reducers file I have:
import { combineReducers } from "redux";
import { routerReducer as router } from "react-router-redux";
import app from "./app";
import invoices from "./invoices/invoices";
import recipients from "./recipients/recipients";
export const makeRootReducer = (asyncReducers) => {
return combineReducers({
router,
app,
invoices,
recipients,
...asyncReducers
})
};
export const injectReducer = (store, { key, reducer }) => {
store.asyncReducers[key] = reducer;
store.replaceReducer(makeRootReducer(store.asyncReducers))
};
export default makeRootReducer;
I have followed many suggestions but I must be missing something.
I found out why. After checking the source code, I saw the following documentation block of persistCombineReducers:
/**
* It provides a way of combining the reducers, replacing redux's #see combineReducers
* #param config persistence configuration
* #param reducers set of keyed functions mapping to the application state
* #returns reducer
*/
export function persistCombineReducers<S>(config: PersistConfig, reducers: ReducersMapObject): Reducer<S & PersistedState>;
The key words were "replacing redux's #see combineReducers".
Removing combineReducers from my reducers file to the following solved the issue:
import { routerReducer as router } from "react-router-redux";
import app from "./app";
import invoices from "./invoices/invoices";
import recipients from "./recipients/recipients";
const reducers = {
router,
app,
invoices,
recipients,
};
export default reducers;
I'm working toward having my React/Redux app update the URL based on actions. I've done quite a bit of looking around. I thought I had a handle on it, but obviously I'm missing something. I have other reducers that respond correctly.
Currently, I'm just trying to log the action.
Routing Reducer
const initialState = { locationBeforeTransitions: null };
export default function routing(state = initialState, action)
{
switch (action.type)
{
case 'LOCATION_CHANGE':
console.log(action)
default:
return state
}
}
Store
/*
Things from other people
*/
import { createStore, applyMiddleware, compose } from 'redux'
import { syncHistoryWithStore } from 'react-router-redux';
import { browserHistory } from 'react-router'
import thunkMiddleware from 'redux-thunk'
import createLogger from 'redux-logger'
/*
Things from us
*/
import { fetchSuitesAndFails, fetchResults } from './actions/actions';
import rootReducer from './reducers/index'
const enhancers = compose(
window.devToolsExtension ? window.devToolsExtension() : f => f
);
const loggerMiddleware = createLogger()
/*
Store
*/
export const store = createStore(
rootReducer,
enhancers,
applyMiddleware(
thunkMiddleware, // lets us dispatch() functions
loggerMiddleware // neat middleware that logs actions
)
);
// Export the history so we can keep track of things
export const history = syncHistoryWithStore(browserHistory, store);
/*
Enable Hot Reloading for the reducers
We re-require() the reducers whenever any new code has been written.
Webpack will handle the rest
*/
if (module.hot) {
// Enable Webpack hot module replacement for reducers
module.hot.accept('./reducers/index', () => {
const nextRootReducer = require('./reducers/index').default
store.replaceReducer(nextRootReducer)
})
}
Index
/*
React router needs
*/
import { combineReducers } from 'redux'
import { routerReducer } from 'react-router-redux'
/*
Reducers
*/
import suite from './suite'
import suitesandfails from './suitesandfails'
import routing from './routing'
/*
Put them all together and return a single reducer
*/
const rootReducer = combineReducers({
suite,
suitesandfails,
routing,
routing: routerReducer
})
export default rootReducer
you can use string "##router/LOCATION_CHANGE" to catch the action.
react-router-redux provides const for that
import { LOCATION_CHANGE } from 'react-router-redux'
....
case LOCATION_CHANGE:
console.warn('LOCATION_CHANGE from your reducer',action)
return state
webpackbin DEMO
routerReducer code from react-router-redux
You can also import the constant from the 'react-router-redux'.
import { LOCATION_CHANGE } from 'react-router-redux'
...
case LOCATION_CHANGE:
console.warn('LOCATION_CHANGE from your reducer',action)
return state