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)
}
}
}
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.
How do I execute custom callback that is passed into an action through react comp, immediately after redux store update.
The idea is say, I trigger an action from react, which will make network request via thunk and dispatches the action with data. This will lead to reducer updating the store. Now, immediately after this I want to redirect to a different page (history.push()) which is a callback.
Using saga middleware it is much easier, but how to implement similar functly using thunk.
You can pass your callback defined in your component the redirect to different page to the thunk and call that after store update is complete. Like this:
function someThunkAction(callback) {
return (dispatch, getState) => {
// Update store logic...
// After update
callback();
};
}
I am facing the problem how to refresh data with redux in my react component after an asynchronous POST-Call. I am already using a redux-middleware which manages my rest calls.
Use-Case:
Inside a react component the user creates a new element (send data asynchronous via REST-API to backend).
Inside the same react component I have a selectable list which now should also contain the new element (with generated Database-ID).
What`s the best react+redux paradigma to update entity in backend and refresh the depending list afterwards?
Best regards Sven
For async operations like making Rest calls, you need a middleware like thunk or saga to dispatch actions based on async response. This is how your create is supposed to work on a high level with a middleware:
dispatch the create action with payload
middleware will issue the rest call to create the object
Based on the response returned, the middleware will dispatch further actions to the store eg. adding the new item to the list.
Thanks to your hints I just found the answer.
Redux (with redux-thunk middleware) can handle my case just out of the box.
Just combine both actions inside one "wrapper action".
Redux will asynchronous execute the "wrapper action".
But due to my "await" statements inside the "wrapper action" my POST and GET Request will be called synchronous in the right order and each updates my redux store as needed.
See: https://github.com/reduxjs/redux-thunk
export const createEntityAndReloadEntities: ICrudPutAction<IEntityProfile> = newEntity => async dispatch => {
return Promise.all[
await dispatch({
type: "CREATE_ENTITY",
payload: axios.post(apiUrl, newEntity)
}),
await dispatch({
type: "FETCH_ENTITY_LIST",
payload: axios.get<IEntityProfile>(`${apiUrl}`)
})];
};
From research, I'm seeing that thunk is the tool you use to be able to chain actions together and/or deal with callbacks, asyc actions, and side-effects.
I'm having trouble understanding the thunk middleware guide. They reference 'store.dispatch' all the time (as do most tutorials on redux) yet I never actually call dispatch and never have access to 'store' so I'm unsure how to implement anything they propose. (I assume this is because I use mapDispatchToProps in react.... which isn't an option in my action creators files)
Below is my action creator (some code removed to clarity):
import { CREATE_NEW_SAMPLING_EVENT } from '../Constants/ActionTypes';
import { emptySamplingEvent } from '../Constants/SamplingEvent';
import _ from 'lodash';
import uuidv4 from 'uuid';
export function createNewSamplingEvent(eventName) {
let newEvent = _.cloneDeep(emptySamplingEvent);
newEvent.eventID = uuidv4();
newEvent.eventName = eventName;
newEvent.dateModified = new Date();
//TODO: call an optional callback
//TODO: dispatch eventLinkTable event
return { type: CREATE_NEW_SAMPLING_EVENT, event: newEvent }
}
What I would like to do is listed in the 'todo's.
I have another action, called EVENT_LINK_TABLE in a different action creator file and different reducer, that would take the uuid from this action creator as an argument. I'd like to dispatch this EVENT_LINK_TABLE action (with the uuid) as soon as I'm done making this a new event.
Further, I'd like to call a standard callback (which will actually be dispatching another action - LOAD_SAMPLNG_EVENT).. but I'm unsure how to call the callback AND return the action. I'm also hearing that doing that from the action creator is bad practice, as well as I don't want it to happen if there is a failure in the creation process.
To add additional info, this is where I'm dispatching the action in my react code:\
handleBrandNewButtonClick = () => {
this.props.createNewSamplingEvent(this.state.newSamplingEventName);
}
This component is 'connect'ed thusly:
const mapStateToProps = function (state) {
return {
samplingEvents: state.SamplingEvents, //to get list of sampling events to check for uniqueness for that user
}
}
const mapDispatchToProps = {
createNewSamplingEvent,
}
export default withRouter(
withStyles(styles, { withTheme: true })
(connect(mapStateToProps, mapDispatchToProps)
(NewEventForm)
)
);
Looks like you don't have proper knowledge in redux. I'll tell you how redux store works. Then you will be able to understand redux.
In redux, we have five things,
Action creator, Action, Dispatcher, Reducers, Store
Imagine you thought to open an insurance company. Ok, here we have our first client. He came to the company and ask for, 'Hey I need to open a new account'.
Now the front desk person will say, 'Ok fill this form and give it to me' Once he gave the form to the front desk, then the person can leave.
In redux, this person is known as Action Creator
The form itself know as the object that returns from action creator
The front-desk person know as Dispatcher
Once the dispatcher got the form, he will make some photocopies of it and send to all departments in your company. policy department, Accounting department, Claims department, etc
These departments are known as reducers
Now each department will check what kind of form is this. Or it's about opening a new account. Ok, Accounting department will get the sum of money and add it to the company vault. Also, the policy department will make a new account for the client. But, the Claims department will not care about this form because it is not about a claim. This is why we set a 'type' property to the object.
Actions creators should only return plain objects. For example, if there is an async call in the action creator it does not return a plain object so we need to have some middleware to avoid this problem. Here we have redux-thunk. This middleware will help us to manually do the dispatch process.
So we get the dispatcher as the parameter to the action creator. Then, once we get the results from the async process, now we can dispatch the results manually inside that action creator. This redux thunk act as a middleware between the dispatcher and the reducers.
You can run business logic inside a function and dispatch the action itself. The action creator, setNewsCreator creates a POJO. That POJO is then dispatched as an action that will get picked up by the reducer
// action creator
const setNewsCreator = ({ news }) => ({ type: 'SET_NEWS', news })
const fetchNews = () => async dispatch => {
const response = await getNews()
dispatch(setNewsCreator({ news: response }))
}
and its usage in a component
componentDidMount() {
this.props.fetchNews() // assuming you've added this function to your mapDispatchToProps
}
Apologies for my first comment. In hindsight, I was not explaining actions correctly.
Lets say i have a form where user is about to click on combination of buttons.
Each button triggers an action of type T and reducer R then updates its state and new combination is rendered on a website.
Now comes the tricky part:
I have my business logic implemented in reducer which applies new state which is about to be rendered. What i need now is when that state accepts a condition, i want to dispatch new action (api request).
What is the right approach to accomplish this kind of problem?
Set a flag into state, and call new action in component after?
Somehow dispatch a function in reducer?
...?
Redux Thunk allows you to dispatch multiple actions and dispatch asynchronous actions inside your action creators. For your scenario, you can do something like this:
function myAction() {
return (dispatch, getState) => {
...
dispatch(firstAction);
const state = getState();
// Check your state conditions from first action here.
dispatch(secondAction);
...
}
}
In this case you could use library redux-saga.
Using redux-saga, you'll be able to create a saga that you call from a component like actions. In the saga you will be able to call several redux-actions. If your state will be valid from the saga, you can call the API request.
A popular alternative to redux-saga is also redux-thunk.