I am trying to learn a react-redux. And in order to call the external rest API, I am using thunk middleware.
However the syntax to so confuses me a lot, I would appreciate if someone can explain me.
export function listTodos() {
return (dispatch) => axios.get("http://localhost:5001/todos")
.then(response => {
console.log(response.data);
dispatch(getTodos(response.data));
})
}
export function _listTodos(todos) {
return {
type: 'LIST_TODOS',
todos: todos
}
}
so in order to get the response from the external API, I am calling listTodos function, which dispatches an action returned by listTodos function.
The part confuses me is Syntax (maybe because it's ES6 an I am not very familiar with that).
how come return is taking a parameter, dispatch?
from where this dispatch is coming, I am not passing anything when I call listTodos
why do I even need a return, shouldn't dispatching an action enough from within then
Redux only accepts plain javascript objects for dispatch. The thunk middleware overcomes this by allowing you to provide functions to the dispatch.
In this example, essentially what you're doing is returning a function reference to the dispatch which will be executed by thunk.
how come return is taking a parameter, dispatch?
from where this dispatch is coming, I am not passing anything when I call listTodos
Once thunk encounters a function it executes that function and passes dispatch as a parameter so that you can use it in the function to actually do the dispatch (_listTodos(todos)).
So thunk will do something like this
if(typeof action === 'function') {
//Reason why it takes a dispatch parameter
//And thunk is the one who sends it to the function
action(dispatch);
}
where action is the function that you have returned from your listTodos()
why do I even need a return, shouldn't dispatching an action enough from within then
return is needed to give thunk the function so that it can perform the above action.
Related
I am using react-redux toolkit with redux thunk to make the api calls. In my app, when I dispatch a thunk for update or delete request and after it succession I want to call the get request as well but only way I was able to figure out is by using a flag with useEffect. Is there any better approach you guys can share?
I also tried this piece in my slice file but it won't work as useDispatch cannot be called inside a javascript function block
.addCase(propertiesThunk.deleteProperty.fulfilled, (state, { payload }) => (
useDispatch(propertiesThunk.getProperties())
))
There's a couple problems here.
The first is that you can never call a React hook outside of a function component, so you definitely can't call useDispatch inside of a reducer.
The second problem is that a reducer can never have side effects. That means it can never dispatch actions. A reducer is only for returning an updated state value.
The simplest answer here would be to have an event handler that does both of these steps back-to-back:
function MyComponent() {
const dispatch = useDispatch()
const handleClick = async () => {
await dispatch(someAsyncThunk())
dispatch(someOtherActionHere())
// or some request, or...
}
}
Another option would be to use the new RTK "listener" side effects middleware, and kick off more logic when some action is dispatched:
startListening({
actionCreator: someAsyncThunk.fulfilled,
effect: (action, listenerApi) => {
// do more work here
}
})
See https://redux-toolkit.js.org/api/createListenerMiddleware for details.
I have a state that specify which component render(component A or B).
This state is determined when my action dispatch specific type(for example GO_TO_B or GO_TO_A).
Then I need to fetch some config from server to render component A. I want these config be in store.So I should call a action(for example fetchConfig() ) to async fetch data from server and dispatch response.
My question is that where i call the fetchConfig() action.
if I call this action in componentDidMount() in component A the error occur that cannot dispatch middle of dispatch.
So which method in react life cycle call after dispatch process and before render ?
I understand you are using redux.
If that's correct, i recommend to do your fetching with a thunk.
redux-thunk is a middleware that allows you to dispatch functions (instead of serialized objetcs like actions), that way you can deley a dispatch of an action or even dispatch it conditionaly.
An example of a thunk would be like that:
function loadSomeThings() {
return dispatch => {
fetchFirstThingAsync.then(data => { // first API call
dispatch({ type: 'FIRST_THING_SUCESS', data }); // you can dispatch this action if you want to let reducers take care of the first API call
return fetchSecondThingAsync(data), // another API call with the data received from the first call that returns a promise
})
.then(data => {
dispatch({ type: 'SECOND_THING_SUCESS', data }); // the reducers will handle this one as its the object they are waiting for
});
};
}
You can notice we can even chain ajax requests and we can dispatch (if we want) an action on each success (OR FAIL!).
I recommend reading the docs to understand it better.
You can use componentWillMount : Doc.
componentWillMount() is invoked immediately before mounting occurs. It is called before render(), therefore setting state synchronously in this method will not trigger a re-rendering. Avoid introducing any side-effects or subscriptions in this method.
In React with Redux, when there are some user operations, e.g., in facebook, user adds some comments, I will call dispatch() to send the add action to redux store, but when should I call back end API to save these data to database? do I need to do it together with dispatch()?
thanks
One solution would be to transfer your API logic into a thunk using a middleware package such redux-thunk (or similar).
Using thunks allows you to treat special kinds of actions as functions which means you can extend a plain action with specific action-related logic. The example you give of needing to serialize your state is an excellent use-case for redux-thunk.
You should note that, unlike reducers, thunks explicitly support fetching state and dispatching subsequent actions via the getState and dispatch functions.
Below is an ES6 example of how such a multi-purpose thunk might look.
To demo the getState() method the new item will only be saved via the api only if the redux state shouldSave value is truthy.
I would also use the async/await syntax to make sure the the api call succeeds before dispatching the local redux action.
Thunk Example - adding a new item
import api from './api'
export const addNew = async (item) => {
return (dispatch, getState) => {
try{
const state = getState()
if(state.shouldSave){
await api.save(item)
}
dispatch({
type: ITEM_ADD_NEW,
data: item
})
}catch(err){
const error = new Error("There was a problem adding the new item")
error.inner=err
throw(error)
}
}
}
I have the following Search Component
import React, { Component } from 'react';
import { Field, reduxForm } from 'redux-form';
import { search } from '../../actions/actions'
class Search extends Component {
render() {
const {handleSubmit, "fields":{query}} = this.props;
return (
<div>
<form onSubmit={handleSubmit(search)}>
<Field className='searchInput' name="query" component="input" type="text" placeholder="Enter course name"/>
<button className='searchSubmit' type='submit' >Search</button>
</form>
</div>
);
}
}
export default reduxForm({
"form":"searchForm",
"fields":["query"]
}, null, {search})(Search);
My search Action is like so
search : function(query){
var data = {
ep:"EP_SEARCH",
payload:{
query: query.query
}
}
getAsynch(data).then(function(result){
return {type: GET_DATA, payload:result};
})
}
I get a response from the endpoint with a result but I need to somehow dispatch the action.
I tried instead of
getAsynch(data).then(function(result){
return {type: GET_DATA, payload:result};
})
this
return function(dispatch){
getAsynch(data).then(function(result){
dispatch({type: GET_DATA, payload:result});
})
}
but it throws an error that dispatch is not defined. Probably because I'm not passing it anywhere in the Component.
Since this is an asynchronous action you will want to use middleware in order to handle the dispatching of multiple actions at different times.
Actions creators are synchronous by nature so this is why middleware is necessary in order to dispatch synchronous actions in the future when the response comes back from your request.
In order to get to grips I would recommend you use redux-thunk as is easier to get to grips with than the main alternative redux-promise middleware library.
Redux Thunk Middleware
Redux Thunk middleware allows you to write action creators that return a function instead of an action.
First set up your redux-thunk middleware during your store configuration
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const store = createStore(
rootReducer,
applyMiddleware(thunk)
);
Now redux-thunk knows how to handle the dispatching of special thunk actions
A thunk actions is just an action that returns another function that takes dispatch as an argument.
Thunk action creator
function fetchThing(url) {
return (dispatch) => {
/*dispatch more actions*/
}
}
The advantage of this is that our components can dispatch asynchronous actions as if they were synchronous and do not need to be passed the dispatch function and therefore don't need to know about the Redux store.
The redux-thunk middleware knows to intercept our thunk action just after we create it, passing in dispatch so future actions such as a RESPONSE_RECEIVED action will have access to dispatch.
In order to use redux-thunk create an action creator which returns a function that accepts dispatch as an argument as per the above.
Example
function epSearch(query){
// thunk passes dispatch into the function below
// so that future actions will have access to dispatch
return function(dispatch){
// below function needs to return a promise in order for us to call .then
getAsynch(data).then(
result => dispatch({ type: GET_DATA, payload:result }),
error dispatch({ type: REQUEST_ERROR, payload: error })
);
};
}
If the promise is not resolved due to an error than our REQUEST_ERROR action will be dispatched and the payload of the action will be the error we received from the rejected promise
Redux Promise and Alternatives to Redux Thunk
Now there are other ways of controlling your async data flow. However redux-thunk is simple and convenience in my opinion. Redux Promise is a a good alternative to Redux Thunk. The difference here is that redux-promise is middleware that intercepts action creators that return promises instead of thunks.
However strictly speaking you don't need middleware at all in redux for async flow. The advantage of it is the centralisation of data flow in one place that allows your components to be more decoupled as they do not need to be explicitly passed the Redux's store's dispatch method and therefore do not care about how and action is created.
For an in depth discussion on middleware in Redux see this answer by Dan Abramov's to the question of why do we need middleware for async flow in redux,
I'm using Redux Thunk, and my actions work properly for everything except ReduxForm. After reading a lot on the topic if discovered the solution.
search : function(query, dispatch){ //APPARENTLY DISPATCH IS THE SECOND PARAMETER, WHICH WAS MISSING IN MY INITIAL FUNCTION
var data = {
ep:"EP_SEARCH",
payload:{
query: query.query
}
}
getAsynch(data).then((result)=>{
dispatch({ type: GET_DATA, payload:result })
});
}
More info on the topic
onSubmit : Function [optional]
The function to call with the form data when the handleSubmit() is
fired from within the form component. If you do not specify it as a
prop here, you must pass it as a parameter to handleSubmit() inside
your form component.
If your onSubmit function returns a promise, the submitting property
will be set to true until the promise has been resolved or rejected.
If it is rejected with a redux-form SubmissionError containing errors
in the form { field1: 'error', field2: 'error' } then the submission
errors will be added to each field (to the error prop) just like async
validation errors are. If there is an error that is not specific to
any field, but applicable to the entire form, you may pass that as if
it were the error for a field called _error, and it will be given as
the error prop.
onSubmit will be called with the following parameters:
values : Object
The field values in the form of { field1: 'value1', field2: 'value2'
}.
dispatch : Function
The Redux dispatch function.
props : Object
The props passed into your decorated component.
http://redux-form.com/6.2.0/docs/api/ReduxForm.md/
TBH I still don't get how dispatch got inside the action creator and why the previous suggestions, which I tried, do not work. A comment from someone more familiar with the topic would be nice.
TBH I still don't get how dispatch got inside the action creator and why the previous suggestions, which I tried, do not work. A comment from someone more familiar with the topic would be nice.
(apologies, not enough rep for a comment)
redux-form's handleSubmit appears to always pass dispatch (as derived from its own props parameter) as the second parameter to the submit function you provide (see https://github.com/erikras/redux-form/blob/master/src/handleSubmit.js)
I would assume this is normally undefined, but thunk, if present, will make dispatch available to redux-form and thus to you.
I'm looking through the auth0 sample project for using react, redux and auth0 for a login scenario here. However I'm a bit confused about this particular example where we call this.props.doAuthentication()
// App.js
import { loginUser, fetchQuote, doAuthentication, fetchSecretQuote } from '../actions'
// add a constructor
constructor(props) {
super(props)
this.props.doAuthentication()
}
Here is the action definition
// actions.js
...
const lock = new Auth0Lock('YOUR_CLIENT_ID', 'YOUR_CLIENT_DOMAIN');
export function login() {
// display lock widget
return dispatch => {
lock.show();
}
}
// Listen to authenticated event and get the profile of the user
export function doAuthentication() {
return dispatch => {
lock.on("authenticated", function(authResult) {
lock.getProfile(authResult.idToken, function(error, profile) {
if (error) {
// handle error
return dispatch(lockError(error))
}
localStorage.setItem('profile', JSON.stringify(profile))
localStorage.setItem('id_token', authResult.idToken)
return dispatch(lockSuccess(profile))
});
});
}
}
...
I'm new to redux so maybe this is an obvious answer but
Where is doAuthentication bound to the the props in App.js? Assuming that App.js is the top level root app component.
Doesn't doAuthentication generate a function that expects a dispatch argument? Why don't we do anything in the constructor with the returned function from doAuthentication()? If we don't assign the returned function to anything, does this.props.doAuthentication persist anything or have any effects? Shouldn't it be something like doAuthentication()(someDispatchFunction) Where does this dispatch function come from?
1.Where is doAuthentication bound to the the props in App.js? Assuming that App.js is the top level root app component.
Ans:
The action doAuthentication is bound with the props of App component using the middleware called redux-thunk.
let createStoreWithMiddleware = applyMiddleware(thunkMiddleware, api)(createStore)
let store = createStoreWithMiddleware(quotesApp)
The above two line of code would have done it for your. Read here why redux-thunk is required.
2.Doesn't doAuthentication generate a function that expects a dispatch argument? Why don't we do anything in the constructor with the returned function from doAuthentication()? If we don't assign the returned function to anything, does this.props.doAuthentication persist anything or have any effects? Shouldn't it be something like doAuthentication()(someDispatchFunction) Where does this dispatch function come from?
Ans:
2.1 Yes, the function which is returned by doAuthentication function expects a dispatch method as to be a first parameter.
2.2, 2.3 Here the doAuthentication action creator's job is to create a Redux action which would just listen for the event called authenticated. When we call doAuthentication action creator, it returns a Redux action function which accepts a dispatch method as first parameter and getState method as second. The parameters would be passed by the redux-thunnk middleware.
The redux-thunk middleware would call the redux action which is return from the doAuthentication call since it is connet-ed. Otherwise we have to dispatch the action returned by the doAuthentication as like the below,
this.props.dispatch(doAuthentication())
2.4 We can do as you mention doAuthentication()(someDispatchFunction), but consider this quote
If Redux Thunk middleware is enabled, any time you attempt to
dispatch a function instead of an action object, the middleware will
call that function with dispatch method itself as the first argument.
And, you can find a detailed info about the redux-thunk with this answer