I'm familiar with react hooks, and i find it really easy to work with useEffect, thunk is very difficult to deal with, can i just use useEffect & axios and just dispatch the result to the store without using createAsyncThunk? is there any major performance benefit to use it over useEffect?
createAsyncThunk:
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
import { userAPI } from './userAPI'
// First, create the thunk
const fetchUserById = createAsyncThunk(
'users/fetchByIdStatus',
async (userId, thunkAPI) => {
const response = await userAPI.fetchById(userId)
return response.data
}
)
// Then, handle actions in your reducers:
const usersSlice = createSlice({
name: 'users',
initialState: { entities: [], loading: 'idle' },
reducers: {
// standard reducer logic, with auto-generated action types per reducer
},
extraReducers: {
// Add reducers for additional action types here, and handle loading state as needed
[fetchUserById.fulfilled]: (state, action) => {
// Add user to the state array
state.entities.push(action.payload)
}
}
})
// Later, dispatch the thunk as needed in the app
dispatch(fetchUserById(123))
useEffect:
import React, { useEffect } from 'react';
import { useDispatch } from 'react-redux'
import { userAPI } from './userAPI'
import axios from 'axios';
function App() {
const dispatch = useDispatch()
useEffect(() => {
axios
.get(userAPI)
.then(response => dispatch({type:'fetchUsers',payload:response.data}));
}, []);
I'm familiar with react hooks, and i find it really easy to work with useEffect, thunk is very difficult to deal with, can i just use useEffect & axios and just dispatch the result to the store without using createAsyncThunk? is there any major performance benefit to use it over useEffect?
createAsyncThunk:
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
import { userAPI } from './userAPI'
// First, create the thunk
const fetchUserById = createAsyncThunk(
'users/fetchByIdStatus',
async (userId, thunkAPI) => {
const response = await userAPI.fetchById(userId)
return response.data
}
)
// Then, handle actions in your reducers:
const usersSlice = createSlice({
name: 'users',
initialState: { entities: [], loading: 'idle' },
reducers: {
// standard reducer logic, with auto-generated action types per reducer
},
extraReducers: {
// Add reducers for additional action types here, and handle loading state as needed
[fetchUserById.fulfilled]: (state, action) => {
// Add user to the state array
state.entities.push(action.payload)
}
}
})
// Later, dispatch the thunk as needed in the app
dispatch(fetchUserById(123))
useEffect:
import React, { useEffect } from 'react';
import { useDispatch } from 'react-redux'
import { userAPI } from './userAPI'
import axios from 'axios';
function App() {
const dispatch = useDispatch()
useEffect(() => {
axios
.get(userAPI)
.then(response => dispatch({type:'fetchUsers',payload:response.data}));
}, []);
Share
Improve this question
asked Apr 11, 2021 at 0:05
tokochitokochi
3063 silver badges7 bronze badges
1
-
2
The question you want to ask yourself is "which part of my app do I want responsible for handling async API calls?" and then use the tools at that level to handle that. If you believe your visual ponent should handle that, then use
useEffect
; if you think your redux state should, then use thecreateAsyncThunk
tool. Separation of responsibilities is very helpful in creating clear boundaries for what a thing should do, and makes for much easier testing and hunting down bugs if they occur. – Derek Commented Apr 11, 2021 at 2:10
3 Answers
Reset to default 7The two setups are essentially similar. You can do the same thing with both approaches.
With the codes exactly as you have them written here, there is a major advantage to the createAsyncThunk
approach because it will catch
any errors that occur in the API call. It will respond to those errors by dispatching a fetchUserById.rejected
action instead of a fetchUserById.fulfilled
action. Your reducer doesn't responded to the rejected
case which is fine. The error is still caught. With your useEffect
you run the risk of "uncaught error in Promise" errors.
Now of course you can catch
the errors on your own. You can also dispatch
a pending
action at the start of the effect. But once you start doing that, the createAsyncThunk
might feel a lot easier by parison since it automatically dispatches pending
, fulfilled
, and rejected
actions.
useEffect(() => {
dispatch({ type: "fetchUsers/pending" });
axios
.get(userAPI)
.then((response) =>
dispatch({ type: "fetchUsers", payload: response.data })
)
.catch((error) =>
dispatch({ type: "fetchUsers/rejected", payload: error.message })
);
}, []);
Instead of returning response.data
just return the api call without the await. That way you have the fulfilled, rejected statuses.
As for your question is a matter of what do you need this data for? Just in this ponent or more than 1 ponent? You can use the context api as well for higher sharing of data among ponents, but to be honest if you are a beginner with react I would understand hooks and react well before you introduce redux. Usually production apps, not to say 100% but the majority, use some kind of state management with async. Also you can look into react-query as another option on how to handle async stuff.
Linda Paiste is correct. Just to extend the answer to your question a bit further, the other very popular option is TanStack Query (React Query). It gives you what you are asking for:
const { isLoading, error, data } = useQuery(['repoData'], () =>
fetch('https://api.github./repos/tannerlinsley/react-query').then(res =>
res.json()
)
)
- It nicely takes care of the caching in
repoData
similar to RTKQ. - It returns the response status
isLoading, error, data
back to you, again just like RTKQ. - You can use RESTful
axios
orfetch
or any non-RESTful request freely. - IMO it is more pact, easier shorter learning curve, without mouthful of weird words for beginners like slice reducer extra redeucer thunk...etc