Is it okay to use Redux Toolkit and Redux Saga together? - redux

is it ok to use the Redux Toolkit, even if I only create Slice in it and solve middleware via Redux Saga?
Or the best practice, in this case, is to use Redux Saga + raw Redux without Toolkit?
Thanks

Redux-saga is just a middleware that handles async logic better. If you have a large-scale app, redux-saga is preferred over redux-thunk. redux-saga makes the testing easier.
the difference between Redux and Redux toolkit is redux toolkit requires less code to set up and redux-toolkit uses immer.js to update the state easier.
To integrate redux-saga with redux-toolkit, you write your sagas and then combine them
import { all } from "redux-saga/effects";
import { firstSaga } from "./firstSaga";
export default function* rootSaga() {
yield all([...firstSaga]);
}
and then in store.js
import { configureStore } from "#reduxjs/toolkit";
import createSagaMiddleware from "redux-saga";
import rootSaga from "./rootSaga";
const sagaMiddleware = createSagaMiddleware();
const store = configureStore({
reducer: {
movie: MovieReducer,
},
middleware: (getDefaultMiddleware) =>
// adding the saga middleware here
getDefaultMiddleware().concat(sagaMiddleware),
});
sagaMiddleware.run(rootSaga);
export default store;

Related

ReduxSaga makes my website re-render infinite

Im using Redux-Saga and Redux-Thunk, this is store I have configured, but it makes my website re-render infinitely. Can u tell me what should i do to solve that? Thank u.
Store.js
import { createStore, applyMiddleware, compose } from 'redux'
import createSagaMiddleware from '#redux-saga/core'
import EmployeeReducer from './reducers/EmployeeReducer'
import thunk from 'redux-thunk'
import axiosMiddleware from "redux-axios-middleware";
import HttpService from "app/services/HttpService";
import RootReducer from './reducers/RootReducer'
import {getEmployeeList} from './saga'
const initialState = {};
const sagaMiddleware = createSagaMiddleware()
const middlewares = [
thunk,
sagaMiddleware,
axiosMiddleware(HttpService.getAxiosClient())
];
export const Store = createStore(
RootReducer,
initialState,
compose(
applyMiddleware(...middlewares)
)
);
sagaMiddleware.run(getEmployeeList)
This is saga.js where i import getEmployeeList
import { call, cancel, put, takeEvery, takeLatest } from 'redux-saga/effects'
import axios from 'axios'
import { GET_EMPLOYEE_LIST } from './actions/EmployeeActions'
import ConstantList from '../../app/appConfig'
const API_PATH = ConstantList.API_ENPOINT + "/employees"
export function* getEmployeeList() {
yield takeEvery(GET_EMPLOYEE_LIST, workEmployeeList)
}
export function* workEmployeeList() {
console.trace("hello");
try {
const url = API_PATH + '/search'
const response = yield call(axios.post, url, {})
yield put({
type: GET_EMPLOYEE_LIST,
payload: response.data.data
})
} catch (error) {
console.log("Request failed!")
}
}
The issue is that you are using the same action type to start the saga & to store the results to redux store.
// here you are waiting for GET_EMPLOYEE_LIST to trigger the saga
yield takeEvery(GET_EMPLOYEE_LIST, workEmployeeList)
// here you are using the same action type to store
// response data to redux store
yield put({
type: GET_EMPLOYEE_LIST,
payload: response.data.data
})
Because of that every time you finish fetching data another saga is triggered.
What you want is to have another action type to store the data in redux store like FETCH_EMPLOYEE_LIST_SUCCESS. Don't forget to update your reducer condition to use the correct action type as well.
You built yourself an infinite loop here:
yield takeEvery(GET_EMPLOYEE_LIST, workEmployeeList)
this means: "every time, GET_EMPLOYEE_LIST is dispatched, execute workEmployeeList"
And in workEmployeeList you have:
yield put({
type: GET_EMPLOYEE_LIST,
payload: response.data.data
})
this means "dispatch GET_EMPLOYEE_LIST".
So 1. leads to 2. and 2. leads to 1.
I can't really suggest anything to fix this as this is missing a lot of building blocks to work in the first place.
But what I can tell you is that we as Redux maintainers really recommend against using sagas for tasks like data fetching.
If you want to know about the backgrounds, I would recommend you watch the evolution of Redux Async Logic.
But generally, seeing you are just getting into Redux, my advice would be to really take a step back and go through our official Redux Tutorial first.
Not only shows it modern (post-2019) Redux which is about 1/4 of the code you are writing here and a lot less confusing, but it also shows multiple different approaches to data fetching.

redux-offline ignore middlewares when executing commit or rollback actions

As shown at https://github.com/redux-offline/redux-offline/pull/178#issuecomment-408795302, we are trying to use with redux-offline a middleware that can dispatch new actions after their counter-parts commit or rollback are executed. Point is that these ones are not dispatched, and after debugging it, we have found that when dispatching the initial action the middleware is being used as the dispatch() function (probably due to how redux composeEnhancers() and applyMiddleware() function works, since they are chaining the functions), but when the commit action is dispatched, it's done using directly the store dispatch() method, so no middleware is being executed at all.
We are not fully sure if it's a fault on our side regarding redux-offline configuration, or a bug in redux-offline itself... Our store configuration is like this:
import { applyMiddleware, compose, createStore } from 'redux'
import reduxOfflineThunkMiddleware from './thunk-middleware'
import rootReducer from '../../reducers'
import { createUser } from '../../actions/user'
const initialState = {}
const windowCompose = (typeof window !== 'undefined') && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
const composeEnhancers = windowCompose || compose
const store = createStore(
rootReducer,
initialState,
composeEnhancers(
applyMiddleware(reduxOfflineThunkMiddleware({ createUser })),
offline()
)
)
Yes, both offline and applyMiddleware are "store enhancers". When you call store.dispatch, the action sequence will be:
Processed by all the middlewares in the middleware pipeline
Processed by offline
Handled by the store itself
Because the offline enhancer is after the applyMiddleware enhancer, any actions that it dispatches internally will never go through the middleware pipeline.

What is the correct way to combine redux-thunk and redux-batched-actions?

What is the correct way to plug redux-batched-actions into my existing Redux store? I am completely confused by the Redux middleware API.
Currently I am using redux-thunk and redux-little-router.
Here is the code source that creates my Redux store:
import { createStore, applyMiddleware, compose, combineReducers } from 'redux'
import thunk from 'redux-thunk'
import { routerForBrowser } from 'redux-little-router'
import reducers from './store'
import routes from './routes'
const { reducer, middleware, enhancer } = routerForBrowser({ routes })
// Combine all reducers and instantiate the app-wide store instance
const allReducers = combineReducers({ ...reducers, router: reducer })
// Build middleware (if devTools is installed, connect to it)
const allEnhancers = (window.__REDUX_DEVTOOLS_EXTENSION__
? compose(
enhancer,
applyMiddleware(thunk, middleware),
window.__REDUX_DEVTOOLS_EXTENSION__())
: compose(
enhancer,
applyMiddleware(thunk, middleware)))
// Instantiate the app-wide store instance
const initialState = window.initialReduxState
const store = createStore(
allReducers,
initialState,
allEnhancers
)
The redux-batched-actions documentation exposes two usages: enableBatching and batchDispatchMiddleware. Which one should I use in my case?
Answering my own question after the return of my expedition into the fabulous source code of redux, redux-thunk, redux-batched-actions, ...
The correct way to do it seems to be using batchDispatchMiddleware, like this:
import { batchDispatchMiddleware } from 'redux-batched-actions'
// ...
const allEnhancers = (window.__REDUX_DEVTOOLS_EXTENSION__
? compose(
enhancer,
applyMiddleware(batchDispatchMiddleware, thunk, middleware),
window.__REDUX_DEVTOOLS_EXTENSION__())
: compose(
enhancer,
applyMiddleware(batchDispatchMiddleware, thunk, middleware)))
Note: I don't know if I could dispatch batched thunks, though. I don't do that in my current application. Use at your own risk!

Integrating redux with aws-appsync

Is there any way to integrate redux with aws-appsync in react-native?
If there is can you give me hint or clue on how to do it? I'm having a hard time integrating it. Thank you in advance.
I think you should be able to connect your own redux store as is detailed in their documentation. Basically create your own and use connect(mapStateToProps, mapDispatchToProps) from react-redux to connect your components.
const MyComponent = props => <h1>HI!</h1>
const ReduxConnected = connect(mapStateToProps, mapDispatchToProps)(MyComponent)
const GraphQLConnected = graphql(gql`query { hi }`)(ReduxConnected)
And then at the root of your application have
import AWSAppSyncClient from "aws-appsync";
import { Provider as ReduxProvider } from 'react-redux'
import { graphql, ApolloProvider } from 'react-apollo';
import { Rehydrated } from 'aws-appsync-react';
import { createStore } from 'redux'
const client = new AWSAppSyncClient({...})
const store = createStore({...})
const ConnectedApp = () =>
<ApolloProvider client={client}>
<Rehydrated>
<ReduxProvider store={store}>
<App />
</ReduxProvider>
</Rehydrated>
</ApolloProvider>
I haven't had a chance to try this setup but I will soon and will edit with any findings. In the meantime here is a link showing how to build a full RN app with AppSync that uses MobX instead of Redux (https://github.com/dabit3/heard) that may also be a good place to start.

Redux-thunk async actions: Use custom middleware for async actions

I am using redux-thunk for async actions and babel-polyfill for promises. I get the following error: Error: Actions must be plain objects. Use custom middleware for async actions.
I solved this problem by including redux-promise in my middleware. I am not sure why I had to use redux-promise to resolve this issue because all examples in Redux docs use babel-polyfill. Should I continue using redux-promise or I might have some issues with babel-polyfill?
babel-polyfill is included in my app entry point:
import 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './components/App.jsx';
import store from './store.jsx';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>
, document.querySelector('.container'));
UPDATE:
So I checked just in case if I have redux-thunk installed. It is in my package.json. Here is my store.js
import thunkMiddleware from 'redux-thunk';
import promise from 'redux-promise'
export default store = createStore(
rootReducer,
defaultState,
applyMiddleware(
thunkMiddleware,
promise
)
);
Here is my async action in action.js:
function receiveStates(json) {
return {
type: RECEIVE_STATES,
states: json.states,
};
}
export function fetchStates(uuid) {
return dispatch =>
fetch(`https://my-api.com/session/${uuid}`)
.then(response => response.json())
.then(json => dispatch(receiveStates(json)));
}
Here is how I call action from component:
fetchStates(sessionID) {
this.props.dispatch(fetchStates(sessionID));
}
# I bind this function in component's constructor
this.fetchStates = this.fetchStates.bind(this);
And finally, this is my reducer:
function statesReducer(state = null, action) {
switch (action.type) {
case RECEIVE_STATES:
return { ...state, states: action.states };
default:
return state;
}
}
I think your error could be caused by:
You are not returning a thunk (a function returning a function of dispatch) from your action creator, so that the Thunk middleware doesn't catch it (maybe you are returning a promise instead?)
You have not installed redux-thunk as suggested by dzeno
I suggest that you install the redux-logger middleware and add it to your store middlewares as the last one, removing the promise middleware which you would not need if returning a thunk.
In this way all the actions are logged in console (previous state, current action, next state) and you can debug what action object type you are returning that is not "digested" by the thunk middleware.
It sounds like you haven't installed / setup redux-thunk yet.
You install the npm package in the usual way:
npm install --save redux-thunk
And here is an example of applying the redux-thunk middleware
getStore.js
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
const getStore = ({combined, initial}) =>{
var store = createStore(combined, initial, applyMiddleware(thunk))
return store
}
export{
getStore
}

Resources