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

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
}

Related

React / Redux - Error: Actions must be plain objects. Use custom middleware for async actions

I am getting this error even though I am using redux thunk. Redux store is also set up correctly (I think). I am creating a MERN app and I want to send a POST request to the backend (form submit) to create a new user. The request and response from the server are fine (checked using Postman). I just can not find where the problem is. The action creator which is causing this is :-
import axios from "axios";
export function signupUser(newuser) {
return function (dispatch) {
axios.post("/auth", newuser).then((res) => {
// dispatch
dispatch({
type: "ADDNEW_USER",
payload: res.data,
});
});
};
}
The store setup is :-
import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
import rootReducer from "./reducers";
const initialState = {};
// const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
compose(
applyMiddleware(thunk),
window.__REDUX_DEVTOOLS_EXTENSION__ &&
window.__REDUX_DEVTOOLS_EXTENSION__()
)
);
export default store;
The Signup.js component is ->
[Signup.js][1]
and the error is -> [here][2]
[1]: https://paste.ubuntu.com/p/gCX3wQPt9X/
[2]: https://imgur.com/a/6SDmydw

Redux/thunk Error: Actions must be plain objects. Use custom middleware for async actions

i'm new to Redux and have followed this video https://www.youtube.com/watch?v=93p3LxR9xfM to implement Redux into my MERN template, however it keeps crashing due to "Error: Actions must be plain objects. Use custom middleware for async actions."
I believe the problem might be somewhere in the dispatch function but can't seem to find it.
fetch file:
`export function fetchPosts () {
return function(dispatch) {
fetch('http://localhost:5000/products/5d082bb89501e113334e5c8e')
.then(res => res.json())
.then(posts => dispatch({
type: FETCH_POSTS,
payload: posts
})
);
}
} `
the component:
class MyComponent extends React.Component {
componentWillMount() {
this.props.fetchPosts();
}
render() {
return (
<div></div>
);
}
};
export default connect(null, { fetchPosts })(MyComponent);
You fetchPosts action returns a function, whereas it was expected to return a plain object.
Returning a function is required for async operations in redux. But you need to wire up a middleware called redux-thunk.
https://github.com/reduxjs/redux-thunk
So that, this error will go away.
How to wire up redux-thunk:
import { createStore, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
import rootReducer from './reducers'
const store = createStore(
reducers, // your reducers
compose(
applyMiddleware(thunk)
)
)

× TypeError: middleware is not a function

image of the ERROR
OK so I'm starting a new project and this is the first time that this has happened to me/ I keep getting an error stating ×
TypeError: middleware is not a function i checked dependencies and everything seems fine gone over my code nothing seems wrong please help
I tried deleting the modules and installing them again, I also checked on a passed application I've been doing and since I'm just starting out the code looks identical but that seems to work.
import { createStore, applyMiddleware } from "redux";
import promiseMiddleware from "redux-promise-middleware";
import userReducer from "./ducks/userReducer";
const middleware = applyMiddleware(promiseMiddleware());
const store = createStore(userReducer, middleware);
export default store;
import React, { Component } from "react";
import routes from "./routes";
import { Provider } from "react-redux";
import store from "./redux/store";
class App extends Component {
render() {
return (
<Provider store={store}>
<div className="App">{routes}</div>
</Provider>
);
}
}
export default App;
When you use the function applyMiddleware, the middlewares shouldn't be called as functions.
So instead of:
const middleware = applyMiddleware(promiseMiddleware());
do:
const middleware = applyMiddleware(promiseMiddleware);
See https://redux.js.org/api/applymiddleware#example-custom-logger-middleware for more details.

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!

update redux reducers after store initialization

I am new to redux architecture, I have this basic doubt can we update the reducers list after creating the store using combinedReducer and createStore methods?
Yes, you can update the reducers and inject a new one asynchronously with replaceReducer api of Redux store.
It is an advanced API. You might need this if your app implements code
splitting, and you want to load some of the reducers dynamically. You
might also need this if you implement a hot reloading mechanism for
Redux.
Take as example this starter-kit
In createStore.js file the reducers passed as arguments to the createStore method are the result of makeRootReducers(). Pay attention to the fact that no one async reducer have been passed to this function.
// extract of src/store/createStore.js
import { applyMiddleware, compose, createStore } from 'redux'
import { routerMiddleware } from 'react-router-redux'
import thunk from 'redux-thunk'
import makeRootReducer from './reducers'
export default (initialState = {}, history) => {
// ...
// ======================================================
// Store Instantiation and HMR Setup
// ======================================================
const store = createStore(
makeRootReducer(), // <------------- without arguments, it returns only the synchronously reducers
initialState,
compose(
applyMiddleware(...middleware),
...enhancers
)
)
store.asyncReducers = {}
// ...
}
In reducers.js file:
makeRootReducer function calls combineReducers with the default reducers
needed for the startup (like router reducer) and other "asynchronously" reducers passed as arguments
injectReducer is a function called for injecting new reducers on runtime. It call replaceReducer api on the store passing as argument a new list of reducers obtain through makeRootReducer(async) function
see below:
// src/store/reducers.js
import { combineReducers } from 'redux'
import { routerReducer as router } from 'react-router-redux'
export const makeRootReducer = (asyncReducers) => {
return combineReducers({
// Add sync reducers here
router,
...asyncReducers
})
}
export const injectReducer = (store, { key, reducer }) => {
store.asyncReducers[key] = reducer
store.replaceReducer(makeRootReducer(store.asyncReducers))
}
export default makeRootReducer
Finally, in the starter-kit the reducer is injected on route definition, like here:
// src/routes/Counter/index.js
import { injectReducer } from '../../store/reducers'
export default (store) => ({
path: 'counter',
/* Async getComponent is only invoked when route matches */
getComponent (nextState, cb) {
/* Webpack - use 'require.ensure' to create a split point
and embed an async module loader (jsonp) when bundling */
require.ensure([], (require) => {
/* Webpack - use require callback to define
dependencies for bundling */
const Counter = require('./containers/CounterContainer').default
const reducer = require('./modules/counter').default
/* ----> HERE <---- */
/* Add the reducer to the store on key 'counter' */
injectReducer(store, { key: 'counter', reducer }) // <-------
/* Return getComponent */
cb(null, Counter)
/* Webpack named bundle */
}, 'counter')
}
This technique is helpful when you want split a large app and avoid to load all the reducers at the boot.

Resources