I have redux saga in my project, but I would like to call reducer action directly from my component.
const mapDispatchToProps = (dispatch) => ({
showUsers: () => dispatch(UserActions.showUsers()),
})
It works fine if showUsers is inside saga
mapDispatchToProps can be a simple object with the action inside
const mapDispatchToProps = {
showUsers: UserActions.showUsers,
};
then you can pass it to connect. connect will call dispatch internally
connect(null, mapDispatchToProps)(MyComponent)
then inside your component, you can call your action
this.props.showUsers()
More info about the different forms of mapDispatchToProps
Related
Say I have a action creator function like the one below:
import {v4 as uuidv4} from uuid;
export const doSomething = (task) => (dispatch) => {
dispatch({
const id = uuidv4();
dispatch({
type: "SET_TASK",
payload: {id, task}
})
})
}
What is the logic behind having to wrap it in a dispatch method when I am calling it to update the state of a store in another action creator function?
i.e.:
import {setAlert} from "./doSomething"
// another action creator
export const anotherActionCreator = () => dispatch => {
...
dispatch(doSomething("Laundry"));
...
}
When I remove the dispatch method wrapping, it would not call the reducer and update the state in the redux store. I am thinking the action is somehow not connected to the store, but I don't understand how. I thought when you call doSomething("Laundry"), the dispatch inside it will already update the store -- but somehow it didn't -- why is that?
By default, the Redux store only understands how to accept plain action objects passed to dispatch, like:
store.dispatch({type: "todos/todoAdded", payload: "Buy milk"};
If you pass a function to dispatch(), the store will throw an error.
However, middleware wrap up the dispatch function, and can intercept whatever's been passed in to dispatch(). This allows middleware to "teach the store how to accept non-action values", such as passing a function to dispatch(someFunction).
This is how the redux-thunk middleware works. It looks for anything that is actually a function instead of an action object, intercepts that function, and calls it.
Is there a purpose to mapDispatchToProps:
const mapDispatchToProps = dispatch => {
return {
addProductToCart: product => dispatch(addProductToCart(product))
}
}
or is it acceptable to just do this:
const mapDispatchToProps = dispatch => {
return {dispatch};
}
and then just call:
props.dispatch() in the component
mapDispatchToProps allows you to simply specify actions your component needs to dispatch. You may take this approach because it's more declarative (letting the mapDispatchToProps handle all the heavy lifting so you can easily call a function from your component) or perhaps you want to share the dispatch function with an unconnected redux component.
In fact, it is perfectly acceptable not to have a mapDispatchToProps function at all.
Additionally based on your example. Instead of doing the following:
const mapDispatchToProps = dispatch => {
return {dispatch};
}
You could simply exclude the mapDispatchToProps function from the connect method like so:
connect(mapStateToProps)(MyComponent)
Then do the following in your component:
props.dispatch({ type: 'MY_ACTION' })}
Without mapDispatchToProps:-
this.props.dispatch(showAlert(message));
With mapDispatchToProps:-
this.props.onShowAlert(message);
THis was the main perpose of mapDispatchToProps
In one of Dan Abramov's answers, there is code
// action creator
function loadData(dispatch, userId) { // needs to dispatch, so it is first argument
return fetch(`http://data.com/${userId}`)
.then(res => res.json())
.then(
data => dispatch({ type: 'LOAD_DATA_SUCCESS', data }),
err => dispatch({ type: 'LOAD_DATA_FAILURE', err })
);
}
// component
componentWillMount() {
loadData(this.props.dispatch, this.props.userId); // don't forget to pass dispatch
}
It seems the mapDispatchToProps just maps a generic dispatch as props (as this.props.dispatch), so this component can dispatch any action at all?
First, is it a good form, or is it just an example but we should make it specific dispatch, such as this.props.dataReceived?
Second, so it looks like for the above code, the mapDispatchToProps would be written as:
const mapDispatchToProps = dispatchOfReduxStore => {
return {
dispatch: dispatchOfReduxStore
}
}
or even just:
const mapDispatchToProps = dispatch => {
return {
dispatch
}
}
and simplified to:
const mapDispatchToProps = dispatch => ({ dispatch })
and this.props.dispatch becomes a versatile dispatch?
I also found that when we simply omit the mapDispatchToProps in connect(), then this.props.dispatch is automatically available.
I think the main reason for binding dispatch to your actions with mapDispatchToProps is to hide redux from your connected component.
Without binding dispatch, your component must know what your actions are and remember that they do nothing without calling them as a parameter to dispatch.
With binding, your component just knows that it has these functions in props that it can simply call when needed.
The other benefit would be arguably cleaner code, since you can just call this.props.myAction() instead of this.props.dispatch(myAction()).
I don't think its a hard right or wrong. I think some of it is preference. I prefer to write my components to where they only know about the props given to them. But you may prefer to make it explicit that your component is using redux to dispatch actions.
import _ from 'lodash';
import jsonPlaceholder from '../apis/jsonPlaceholder';
export const fetchPostsAndUsers = () => async (dispatch, getState) => {
await dispatch(fetchPosts());
_.chain(getState().posts)
.map('userId')
.uniq()
.forEach(id => dispatch(fetchUser(id)))
.value();
};
export const fetchPosts = () => async dispatch => {
const response = await jsonPlaceholder.get('/posts');
dispatch({ type: 'FETCH_POSTS', payload: response.data });
};
In the above code getState and dispatch functions are passed as arguments to the action creator function, what i m puzzled about is why are these functions not imported from anywhere or does react/redux somehow import them for us?
ok I will try to clear your confusion,
As you know action creators returns plain javascript object, but thunk is a middleware which allows you to return function instead of plain javascript object from the action creators, so when you use thunk if you return plain javascript object from action creator its handled in normal way, but when you return a function from action creator than thunk handle it and call this function with dispatch and getState, so you can dispatch an action asynchronously, you are not passing these arguments, see it this way that you are returning a callback from action creator and thunk call this callback with these arguments.
Hope it helps.
When you connect a react component with redux using a connect function provided by redux you will pass in to functions: mapStateToProps and mapDispatchToProps. Those will be the parameters your looking for (dispatch and getState).
Thunk is a function which optionally takes some parameters and returns another function, it takes dispatch and getState functions, and both of these are supplied by Redux Thunk middleware
I'm using typescript-fsa and typescript-fsa-reducers packages to simply create actions and reducers in TypeScript React application.
const actionCreator = actionCreatorFactory();
export function signInHandler(state: UserState, action: Action): UserState {
// ????
return { ...state };
}
export const signIn = actionCreator.async<SignInRequest, RequestResponse<SignInResponse>>("USER_SIGNIN");
export const UserReducer = reducerWithInitialState({ signedIn: false } as UserState)
.casesWithAction([signIn.started, signIn.done], signInHandler)
.build();
Usage in component:
export default connect<StateProps, DispatchProps>(
(state: RootState) => ({} as StateProps),
(dispatch: Dispatch<RootState>) => {
return {
signIn: (userName: string, password: string) => dispatch(signIn.started(new SignInRequest(userName, password)))
};
}
)(SignIn);
And now I'm stuck. I don't know how to make HTTP calls to my API so I can send request when component dispatches action on dispatch next action when response from API arrives. I would like to use promises.
How to solve that?
In React without the typescript-fsa abstraction, you'd make async API callsat the action creator level, since actions are just dispatched POJOs and reducers are supposed to not have any side effects.
There are two projects that make it easy to do this, redux-thunk and redux-saga. I prefer redux-thunk because it is easier to wrap your head around. Basically your action creators get passed the dispatch function, and then they can be responsible for dispatching more than one thing... like so:
function asyncActionCreator(dispatch) {
dispatch(startAsyncAction());
doSomethingAsync()
.then(result => dispatch(completeAsyncAction(result))
.catch(err => dispatch(errorAsyncAction(err));
}
In your typescript-fsa world, there are some companion packages for both of these: typescript-fsa-redux-thunk and typescript-fsa-redux-saga.
It appears that typescript-fsa-redux-thunk takes a similar approach to the above example, using the concept of an "action worker", which coordinates the dispatching of actions via typescript-fsa. There is a really good example of doing this on the typescript-fsa-redux-thunk repo.