Can I Have Redux-Saga and Redux-Thunk Working Together? - redux

I was working with redux-saga but I'm with a problem: the redux-auth-wrapper needs the redux-thunk to do the redirects, so I simply added the thunk in my store:
import {createStore, compose, applyMiddleware} from 'redux';
import createLogger from 'redux-logger';
import {routerMiddleware} from 'react-router-redux';
import {browserHistory} from 'react-router';
import thunk from 'redux-thunk';
import createSagaMiddleware, {END} from 'redux-saga';
import sagas from '../sagas';
import reduxImmutableStateInvariant from 'redux-immutable-state-invariant';
import rootReducer from '../reducers';
import _ from 'lodash';
import {loadState, saveState} from '../connectivity/localStorage';
const persistedState = loadState();
const routerMw = routerMiddleware(browserHistory);
const loggerMiddleware = createLogger();
const sagaMiddleware = createSagaMiddleware();
function configureStoreProd() {
const middlewares = [
// Add other middleware on this line...
routerMw,
sagaMiddleware,
thunk
];
const store = createStore(rootReducer, persistedState, compose(
applyMiddleware(...middlewares)
)
);
store.subscribe(_.throttle(() => {
saveState({
auth: store.getState().auth
});
}, 1000));
sagaMiddleware.run(sagas);
store.close = () => store.dispatch(END);
return store;
}
function configureStoreDev() {
const middlewares = [
// Add other middleware on this line...
// Redux middleware that spits an error on you when you try to mutate your state either inside a dispatch or between dispatches.
reduxImmutableStateInvariant(),
routerMw,
sagaMiddleware,
loggerMiddleware,
thunk
];
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; // add support for Redux dev tools
const store = createStore(rootReducer, persistedState, composeEnhancers(
applyMiddleware(...middlewares)
)
);
store.subscribe(_.throttle(() => {
saveState({
auth: store.getState().auth
});
}, 1000));
if (module.hot) {
// Enable Webpack hot module replacement for reducers
module.hot.accept('../reducers', () => {
const nextReducer = require('../reducers').default; // eslint-disable-line global-require
store.replaceReducer(nextReducer);
});
}
sagaMiddleware.run(sagas);
store.close = () => store.dispatch(END);
return store;
}
const configureStore = process.env.NODE_ENV === 'production' ? configureStoreProd : configureStoreDev;
export default configureStore;
This way works nice without errors, but I'm new in react and I don't know if have problem with redux-saga and redux-thunk working together...
Someone can help me?

No problems to have both. Sagas are just background checkers who react to some actions while thunk let's you have more interesting action creators.
While thunk will act more like synced code, sagas will do it's job in a background.
Both extensions do not change how actions are flying around. Actions still, in the end, are just bare objects like w/o thunk or w/o sagas.

Yes, of course you can use both redux-saga and redux-thunk in this way,
import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'
import thunk from 'redux-thunk'
import logger from 'redux-logger'
import rootSagas from './sagas'
import rootReducer from './reducers'
const saga = createSagaMiddleware()
const middleWares = [saga, thunk]
export const store = createStore(
rootReducer,
applyMiddleware(...middleWares)
)
saga.run(rootSagas)

Related

Migrating from Vite with ReactJS to Nexjs, SyntaxError: Cannot use import statement outside a module

I am migrating a vite and react project to Nextjs. This project uses redux-persist.
When migrating and running code it shows this error.
I was trying several solutions found on the net but none helped.
I created a new NextJS project from scratch and ended with the same error.
import { FLUSH, PAUSE, PERSIST, PURGE, REHYDRATE, DEFAULT_VERSION } from './constants';
SyntaxError: Cannot use import statement outside a module
My _app.tsx code
import { persistor, store } from "app/store";
import { Provider } from "react-redux";
function MyApp({ Component, pageProps }: AppProps) {
return (
<Provider store={store}>
<Component {...pageProps} />
</Provider>
);
}
export default MyApp;
app/store.ts
import { configureStore, combineReducers } from "#reduxjs/toolkit";
import {
persistStore,
persistReducer,
FLUSH,
REHYDRATE,
PAUSE,
PERSIST,
PURGE,
REGISTER,
} from "redux-persist";
import storage from "redux-persist/lib/storage";
import logger from "redux-logger";
import rootReducers from "./rootReducers";
const isDev = process.env.NODE_ENV === "development";
const persistConfig = {
key: "root",
version: 1,
storage,
whitelist: ["darkmode"],
};
const rootReducer = combineReducers(rootReducers);
const persistedReducer = persistReducer(persistConfig, rootReducer);
const middlewareLogger: any = !!isDev ? logger : [];
export const store = configureStore({
reducer: persistedReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
},
}).concat(middlewareLogger),
});
export let persistor = persistStore(store);
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
Someone got it to work with NextJS?
Thanks!

setup saga middleware with redux-starter-kit's configureStore()

I am working on the application which is purely redux-saga, but as the application is growing, the number of files is also growing. To solve this issue I am trying to setup redux-starter-kit to my current application.
Here is my store configuration file index.js
import { configureStore, getDefaultMiddleware } from 'redux-starter-kit'
import rootReducer from '../reducers'
export const store = configureStore({
reducer: rootReducer,
middleware: [...getDefaultMiddleware()]
})
old set up for just redux-saga without redux-starter-kit
// import createSagaMiddleware from 'redux-saga'
// import { initSagas } from '../initSagas'
// import rootReducer from '../reducers'
// import { loadState, saveState } from './browserStorage'
// function configureStore () {
// const sagaMiddleware = createSagaMiddleware()
// const persistedState = loadState()
// const createdStore = createStore(
// rootReducer,
// persistedState,
// applyMiddleware(sagaMiddleware)
// )
// initSagas(sagaMiddleware)
// return createdStore
// }
// export const store = configureStore()
// store.subscribe(() => {
// saveState(store.getState())
// })
the problem:
when I set up the redux-starter-kit the old sagas are not working.
Long story short:
How can I set up my existing redux-saga application with redux-starter-kit, without disturbing the current saga files?
Thank you in advance.
redux-starter-kit does not include sagaMiddleware by default [1]. You'll need to add it to the middleware list and initialize the sagas yourself.
In your case I believe this should work:
import createSagaMiddleware from 'redux-saga'
import { configureStore, getDefaultMiddleware } from 'redux-starter-kit'
import rootReducer from '../reducers'
import { initSagas } from '../initSagas'
const sagaMiddleware = createSagaMiddleware();
export const store = configureStore({
reducer: rootReducer,
middleware: [...getDefaultMiddleware(), sagaMiddleware]
})
initSagas(sagaMiddleware);
[1] https://redux-starter-kit.js.org/api/getdefaultmiddleware

How to run redux devtools with redux saga?

Trying to run reduxdevtools with redux saga:
Getting this error:
Error
Before running a Saga, you must mount the Saga middleware on the Store using applyMiddleware
This is my jscode:
const store = createStore(
reducer,
applyMiddleware(sagaMiddleware),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
How can I run this devtool with saga? Alternatively what would work otherwise?
codepen
I've used redux-devtools-extension package as described here, redux-devtools-extension documentation.
After adding the package, I've replaced the store definition with this:
const store = createStore(
reducer,
composeWithDevTools(
applyMiddleware(sagaMiddleware)
)
);
Fixed Codepen Link
The previous answer (by trkaplan) uses an imported method composeWithDevTools from 'redux-devtools-extension' package.
If you don't want to install this package, you may use this code (based on the docs):
const composeEnhancers = typeof window === 'object' && window['__REDUX_DEVTOOLS_EXTENSION_COMPOSE__'] ?
window['__REDUX_DEVTOOLS_EXTENSION_COMPOSE__']({ }) : compose;
const enhancer = composeEnhancers(
applyMiddleware(thunkMiddleware, sagaMiddleware, /*other middleware*/),
/* other store enhancers if any */
);
const emptyReducer = () => {};
const store = createStore(emptyReducer, enhancer);
This is how you configure your redux, redux-devtool-extension and redux-saga for the real projects..
import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import createSagaMiddleware from 'redux-saga';
import rootReducer from '../reducers';
import rootSaga from '../sagas';
const configureStore = () => {
const sagaMiddleware = createSagaMiddleware();
return {
...createStore(rootReducer, composeWithDevTools(applyMiddleware(sagaMiddleware))),
runSaga: sagaMiddleware.run(rootSaga),
};
};
export default configureStore;
Incase Compose of Redux is used. Then below code is useful.
Step 1: Add chrome Redux DevTools extension.
step 2: npm install redux-devtools-extension.
import { composeWithDevTools } from 'redux-devtools-extension';
const store = createStore(
reducer,
compose(
applyMiddleware(sagaMiddleware),
composeWithDevTools(),
),
);

How to add `redux-logger` on middlewares?

I want to add redux-logger on the middlewares chain. Below is my code:
import {createStore, combineReducers, applyMiddleware, compose} from 'redux';
import reducers from './index';
import createLogger from 'redux-logger';
import thunk from 'redux-thunk';
const logger = createLogger ({
log: 'info',
});
// create the global store
const store = compose (applyMiddleware (thunk, logger)) (createStore) (
reducers
);
export default store;
I will get below error with above code:
applyMiddleware.js:39 Uncaught TypeError: middleware is not a function
at applyMiddleware.js:39
at Array.map (<anonymous>)
at applyMiddleware.js:38
It works fine if I change the apply middleware to this line:
applyMiddleware (thunk, createLogger)
but I need to create the logger with some specific parameters. How can I add the created logger into middleware chain?
import { applyMiddleware, compose, createStore } from 'redux';
import reducers from './index';
import thunk from 'redux-thunk';
import { logger } from 'redux-logger';
const composeEnhancers = typeof window === 'object' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
const middlewareList = [thunk, logger]
const enhancer = composeEnhancers(
applyMiddleware(...middlewareList)
);
const store = createStore(reducers, enhancer);
export default store;
It should working fine if you change your store into this:
const store = createStore(reducers, compose(applyMiddleware(thunk, logger)));
If that doesn't work, take a look into this issue. It should do the same as above code I think.
https://github.com/gaearon/redux-thunk/issues/35
Fixed this issue by changing the import to import {createLogger} from 'redux-logger';.

React native persist and encrypt user token - Redux-persist-transform-encrypt error

It seems there is an issue using the encrypt option of redux-persist in react-native:
https://github.com/maxdeviant/redux-persist-transform-encrypt/issues/15
Can anyone help with any solution/workaround to use redux persist to encrypt and store a login token in react-native?
When I try to use the redux persist with the redux-persist-transform-encrypt i get
Redux-persist-transform-encrypt: expected outbound state to be a string error
import { createStore, compose, applyMiddleware } from 'redux';
import ReduxThunk from 'redux-thunk';
import { persistStore, autoRehydrate } from 'redux-persist';
import { AsyncStorage } from 'react-native';
import createEncryptor from 'redux-persist-transform-encrypt';
import reducers from './reducers';
const store = createStore(
reducers,
{},
compose(
applyMiddleware(ReduxThunk),
autoRehydrate(),
),
);
const encryptor = createEncryptor({
secretKey: 'my-super-secret-key-999',
});
persistStore(
store,
{
storage: AsyncStorage,
whitelist: ['auth'],
transforms: [encryptor],
},
);
export default store;
My auth state is something like this:
const INITIAL_STATE = {
user: null,
token: ''
};
Is there any solution to use redux-persist-transform encrypt or the transform and other package to encrypt the token when using redux persist?
I found a solution using the customTransform instead of redux-persist-transform-encrypt:
import { createStore, compose, applyMiddleware } from 'redux';
import ReduxThunk from 'redux-thunk';
import { persistStore, createTransform, autoRehydrate } from 'redux-persist';
import { AsyncStorage } from 'react-native';
import CryptoJS from 'crypto-js';
import reducers from './reducers';
const store = createStore(
reducers,
{},
compose(
applyMiddleware(ReduxThunk),
autoRehydrate(),
),
);
const encrypt = createTransform(
(inboundState, key) => {
if (!inboundState) return inboundState;
const cryptedText = CryptoJS.AES.encrypt(JSON.stringify(inboundState), 'secret key 123');
return cryptedText.toString();
},
(outboundState, key) => {
if (!outboundState) return outboundState;
const bytes = CryptoJS.AES.decrypt(outboundState, 'secret key 123');
const decrypted = bytes.toString(CryptoJS.enc.Utf8);
return JSON.parse(decrypted);
},
);
persistStore(
store,
{
storage: AsyncStorage,
whitelist: ['auth'], // <-- keys from state that should be persisted
transforms: [encrypt],
},
);
export default store;
When using redux-persist initial state is triggered before rehydrating is finished so I had to apply this too:
https://github.com/rt2zz/redux-persist/blob/master/docs/recipes.md#delay-render-until-rehydration-complete
This worked for me:
import { createStore } from 'redux';
import { persistStore, persistReducer } from 'redux-persist';
import createEncryptor from 'redux-persist-transform-encrypt';
import storage from 'redux-persist/lib/storage';
import rootReducer from '/path/to/your/rootReducer';
const encryptor = createEncryptor({
secretKey: 'omg-this-is-some-secret-stuff',
});
const persistConfig = {
key: 'root',
storage,
transforms: [encryptor],
};
const reducer = persistReducer(persistConfig, rootReducer);
export const store = createStore(reducer);
export const persistor = persistStore(store);
This is working fine and store data in session storage.
Use below to encript redux data.
1) install redux-persist-transform-encrypt module
yarn add redux-persist-transform-encrypt
npm install redux-persist-transform-encrypt --save
2) import redux files.
import { createStore } from 'redux';
import { persistStore, persistReducer } from 'redux-persist';
import createEncryptor from 'redux-persist-transform-encrypt';
import storageSession from 'redux-persist/lib/storage/session';// store data in session storage
// import storage from 'redux-persist/lib/storage'; // store data in local storage
import promise from 'redux-promise';
import rootReducer from './path/reducers/';
const encryptor = createEncryptor({
secretKey: 'test#key12',
});
const persistConfig = {
key: 'root',
storage:storageSession,
// storage:storage,
transforms: [encryptor],
}
const persistedReducer = persistReducer(persistConfig, rootReducer)
export default () => {
let store = createStore(persistedReducer)
let persistor = persistStore(store)
return { store, persistor }
}

Resources