export default function applyMiddleware(...middlewares) {
return (createStore) => (...args) => {
const store = createStore(...args)
let dispatch = () => {
throw new Error(
'Dispatching while constructing your middleware is not allowed. ' +
'Other middleware would not be applied to this dispatch.'
)
}
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args),
}
const chain = middlewares.map((middleware) => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch,
}
}
}
This code is the actual implementation of applymiddleware form https://github.com/reduxjs/redux/blob/4.x/src/applyMiddleware.js . In this code mapping a middlewares passing middlewareAPI as argument to each middelware
let's take example thunk function
In that thunk function I am getting a dispatch and getState as arguments to handle async actions
please observe my question i am try to understand my doubt clearly
In that applyMiddleware function each middleware is passing a middlewareAPI as argument but inside middlewareAPI object dispatch is a some anonymous function returning a other function named as disptach. In the place of store we are passing a middlewareAPI object But how thunk function is getting a composed disptach as argument
My Doubts:
1.dispatch: (...args) => dispatch(...args) what is the meaning to this line and how it's going help in that code
2.we are passing middlewareAPI in the place of store object before store.dispatch function is updating and my thunk function is getting arguments as diapatch and getState. From redux-thunk my thunk function will be calling with diapatch and getState arguments. How dispatch function is getting updated dispatch function as argument to my thunk function
Related
I'm new to react, redux and tyring to understand the redux-toolkit tutorial i follow. I have the slice as follows.
const initialState = {
count: 0,
};
export const counterSlice = createSlice({
name: "counter",
initialState,
reducers: {
increment: (state) => {
state.count += 1;
},
},
});
export const { increment } = counterSlice.actions;
export default counterSlice.reducer;
export const incrementTest = () => (dispatch) => {
dispatch(increment());
};
Then I use that incrementTest action as follows
<button onClick={() => dispatch(incrementTest())}> + </button>
I want to understand following.
In following fuction
export const incrementTest = () => (dispatch) => {
dispatch(increment());
};
we return a function which takes argument as dispatch then call that provided dispatch function with argement of another function increment which is defined above and exported.
However when we call this function we use dispatch(incrementTest()) providing incrementTest as a param to dispatch. I don't understand this concept . Which concept in javascript should i further study to learn this ?
Also increment reducer take state as parameter ( and action also in some cases ). Who provide this (state,action) to this function as we call it as dispatch(incrementTest())
So this:
export const incrementTest = () => (dispatch) => {
dispatch(increment());
};
is an example for a thunk, a function that gets called (by a redux middleware) with two arguments, dispatch and getState. It is typically used to coordinate async work since you can await stuff inside the function or deal with promises. Look up the thunk middleware if you want to know more. I'm not sure why they made this action a thunk, there's no need for it, maybe testing.
To your second question, the library does. All you do is call dispatch() with an action, the library calls the reducer function with the current state and your action. Think of it as emitting an event. Your job is to create the event, the library takes care of updating the global state accordingly. The reducer is written declaratively, sort of. As in "how would the global state need to change if that specific event happened?".
Store enhancers seem like extension methods in C#, since they add functionality to a Redux data store. Although in most projects, middlewares are enough to modify the behavior of the data store, but the question is:
when is it best to define a store enhancer?
how the input to a store enhancer is provided?
Let's consider the following sample enhancer which tends to dispatch actions asynchronously:
export const asyncEnhancer = delay => createStoreFunction => (...args) => {
const store = createStoreFunction(...args);
return {
...store,
dispatchAsync: (action) => new Promise((resolve, reject) => {
setTimeout(() => {
store.dispatch(action);
resolve();
}, delay);
})
};
}
In the above code, what value is passed to createStoreFunction and args ?
As for applying this enhancer, imagine we have:
export default createStore(myCombinedReducer,
compose(applyMiddleware(m1, m2, thunk), asyncEnhancer(2000)))
The question above, included a simple store enhancer just to dispatch actions asynchronously.
As a Redux API, applyMiddleware is also considered as store enhancer and is the only store enhancer that's included in the Redux library which enables you to wrap the store's dispatch method. According to the documentation, the store enhancer signature is :
createStore => createStore
As a result, having a look at the implementation code of applyMiddleware from docs, will give a great idea on how the input to a store enhancer is provided:
export default function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args)
let dispatch = () => {
throw new Error(
'Dispatching while constructing your middleware is not allowed. ' +
'Other middleware would not be applied to this dispatch.'
)
}
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
const chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
The code above, clearly shows the signature createStore => createStore.
applyMiddlaware API returns a function and as soon as you input a createStore into it, it expects you to provide the arguments similar to what is usually provided for createStore API.
So, that's why we can use this shorter notation:
const myStore=applyMiddleware(thunk,logger)(createStore)(rootReducer)
than this longer one:
const middlewares = [
thunk,
logger
]
const myStore = createStore(
rootReducer,
initialState
compose(applyMiddleware(...middlewares))
)
Although in most projects, writing middleware is satisfactory enough, but it's useful to have a high-level understanding of what a store enhancer is.
I'd set redux custom middleware, and change on action to insert argument on action. But the result is actions must be plain objects. use custom middleware for async actions because in action i did't return dispatch(myAction)
my configure middleware
const injectMiddleware = ({dispatch, getState}) => next => action => {
//skipped all my logic
return(
typeof action === 'function' ?
next(action({dispatch, getState, ...anotherCustomFunction}))
:
next(action)
)
}
my Actions
export const setUserSessionToken = () => ({dispatch}: Store) => {
dispatch(setToken)
}
and get Error
actions must be plain objects. use custom middleware for async actions
fixed with return
export const setUserSessionToken = () => ({dispatch}: Store) => {
return dispatch(setToken)
}
and no error without return if not using custom middleware
export const setUserSessionToken = () => (dispatch) => {
dispatch(setToken)
}
or in custom middleware just do
return next(action)
Change this
next(action({dispatch, getState, ...anotherCustomFunction}))
as
action({dispatch, getState, ...anotherCustomFunction})
calling next is passing an action object to the next middleware. Of course, you can pass a function that not evaluated yet, but you will need another middleware like thunk to handle the function eventually.
I'm trying to pass in a variable into an axios request in my action:
export function searchRequest(search){
return(dispatch)=>{
console.log('in search', search)
return axios.get(`http://localhost:4000/reports/${search}`)
.then(response => {
dispatch(searchInfo(response.data))
})
}
}
When I console log search, it does not register.
However, when I remove the return dispatch and console log response.data in the .then, I get the desired data, but I'm not able to use dispatch.
The question is, why am I not able to pass in search in this way?
Edit: this is in react native
I'm not sure how you connect this function to Redux in React-native but I think it works like you're doing it with regular React.
The searchRequest function return a new function with one variable dispatch. search will be "static" with the value you've passed in when calling searchRequest, so probably null or undefined or some hardcoded value.
You should to this the other way around:
import {connect} from 'react-redux'
const searchRequest = (dispatch) => {
return async (search) => {
const response = await axios.get(`http://localhost:4000/reports/${search}`)
dispatch(searchInfo(response.data))
}
}
class YourComponent extends Component {
....
onSearch = (search) => {
this.props.doSearch(search)
}
....
}
const mapDispatch = (dispatch) => ({
doSearch: searchRequest(dispatch)
})
export connect(null, mapDispatch)(YourComponent)
This is how it looks with "normal web"-React. Maybe in native it looks different. Notice that dispatch is provided as parameter once. Afterwards when calling doSearch the same instance of dispatch is used inside the function.
I am trying to translate the following from ES2015 to vanilla javascript:
fileA.js
export const checkout = ({ dispatch }) => {
dispatch(types.CHECKOUT_REQUEST)
}
fileB.js
import checkout;
checkout();
So far (using https://babeljs.io/repl/) I have:
fileA2015.js:
module.exports = {
checkout: function (_ref) {
dispatch = _ref.dispatch;
dispatch(types.CHECKOUT_REQUEST)
}
};
But I cannot figure out what to pass to checkout in fileB to have access to the correct _ref. What is this and where does it come from?
export const checkout = ({ dispatch }) => {
dispatch(types.CHECKOUT_REQUEST)
}
In the above function you are destructuring an object, which is expected to have a dispatch property. Where in dispatch is a callback function.
when you pass in the data object as a parameter, because of { dispatch }, which destructures the object that is passed in, it is expected to have dispatch property.
var data = {
dispatch: (type) => {
.......
}
};
So when checkout method is referenced and invoked in fileB.js, then you will have to pass in the data object at this point.
fileB.js
var data = {
dispatch: (type) => {
.......
}
};
import checkout;
checkout(data);
Let's note a few things here:
export const checkout = ({ dispatch }) => {
dispatch(types.CHECKOUT_REQUEST)
}
In the code above, checkout() is a function that expects a single parameter -- specifically an object with the property dispatch. dispatch is also expected to be a function.
If you don't pass any parameters to checkout() (or an object that doesn't have a dispatch property), dispatch will be undefined.
Therefore you need to pass something like:
checkout({ dispatch: function(){} })