简体   繁体   中英

Redux Toolkit: How to make a GET request after POST or PUT request is successful?

I made a simple To Do list application to learn Redux Toolkit and after getting it working without a backend, decided to add a simple Flask server to practice working with Redux Toolkit's createAsyncThunk

I was successfully able to create 3 different async thunks for when a user fetches their to-dos, posts a new to-do, or puts a to-do. Here is my toDoSlice :

import { createSlice, current, createAsyncThunk } from '@reduxjs/toolkit';
import axiosPrivate from '../../api/axios';

const initialState = {
    toDos: []
}

export const getToDos = createAsyncThunk('toDo/getToDos', async (user) => {
    const response = await axiosPrivate.get(`/to-dos/${user?.user?.firebase_uid}`, { headers: { 'Authorization': user?.idToken }, withCredentials: true });
    return response.data
})

export const addToDo = createAsyncThunk('toDo/addToDo', async ({ user, newToDo }) => {
    const response = await axiosPrivate.post('/to-dos/new', { userId: user?.user?.id, firebaseUid: user?.user?.firebase_uid, task: newToDo?.task }, { headers: { 'Authorization': user?.idToken }, withCredentials: true });
    const data = response.data
    return {data, user}
})

export const updateToDo = createAsyncThunk('toDo/updateToDo', async ({ user, toDoId, updateAttributes }) => {
    const response = await axiosPrivate.put('/to-dos/update', { userId: user?.user?.id, firebaseUid: user?.user?.firebase_uid, toDoId: toDoId, updateAttributes},  { headers: { 'Authorization': user?.idToken }, withCredentials: true })
    const data = response.data
    return {data, user}
})

export const toDosSlice = createSlice({
    name: 'toDos',
    initialState,
    reducers: {},
    extraReducers(builder) {
        builder
            .addCase(getToDos.fulfilled, (state, action) => {
                state.toDos = action.payload?.toDos
            })
            .addCase(addToDo.fulfilled, (action) => {
                getToDos(action?.payload?.user); // This does not change anything
            })
            .addCase(updateToDo.fulfilled, (action) => {
                getToDos(action?.payload?.user); // This does not change anything
            })
    }
})

export const { deleteToDo } = toDosSlice.actions;

export const selectToDos = (state) => state.toDos;

export default toDosSlice.reducer;

The problem I am having, is that after a user edits their toDo by marking it complete, I am unsure of where and how to properly fetch the to-dos from the backend. I know that I could technically set the state of the toDo using the redux state and validating if the POST or PUT was successfully, although would like to learn how it is properly done with a GET request thereafter.

My ToDoList component where users can DELETE or PUT their ToDos is as follows:

import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import Stack from '@mui/material/Stack';
import Divider from '@mui/material/Divider';
import Box from '@mui/material/Box';
import Grid from '@mui/material/Grid';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemButton from '@mui/material/ListItemButton';
import IconButton from '@mui/material/IconButton';
import DeleteIcon from '@mui/icons-material/Delete';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import Checkbox from '@mui/material/Checkbox';
import Typography from '@mui/material/Typography';

import { getToDos, updateToDo, selectToDos } from '../toDoSlice';
import useAuth from '../../../hooks/useAuth';

function ToDoList() {
    const dispatch = useDispatch();
    const { user } = useAuth();
    const toDos = useSelector(selectToDos);

    useEffect(() => {
        dispatch(getToDos(user));
    }, [dispatch, user])

    const handleChange = (toDoId, updateAttributes) => {
        dispatch(updateToDo({ user, toDoId, updateAttributes }));
        // dispatch(getToDos(user)); // This sometimes causes the GET request to occur before the PUT request, which I don't understand    
    }

    return (
        <Stack spacing={2}>
            <Divider sx={{ marginTop: 5 }} />
            <Grid xs={4} item>
                {toDos?.toDos?.length ?
                    <Box>
                        <List>
                            {toDos?.toDos?.map((toDo) => (
                                <ListItem
                                    key={toDo?.id}
                                    secondaryAction={
                                        <IconButton
                                            edge="end"
                                            onClick={() => handleChange(toDo?.id, { 'deleted': !toDo?.deleted })}
                                        >
                                            <DeleteIcon />
                                        </IconButton>
                                    }
                                >
                                    <ListItemButton onClick={() => handleChange(toDo?.id, { 'completed': !toDo?.completed })}>
                                        <ListItemIcon>
                                            <Checkbox
                                                name={toDo.task}
                                                checked={toDo.completed}
                                                edge='start'
                                            />
                                        </ListItemIcon>
                                        <ListItemText
                                            primary={toDo.task}
                                            sx={{ textDecoration: toDo.completed ? 'line-through' : null }}
                                            primaryTypographyProps={{
                                                style: {
                                                    whiteSpace: 'nowrap',
                                                    overflow: 'hidden',
                                                    textOverflow: 'ellipsis'
                                                }
                                            }}
                                        />
                                    </ListItemButton>
                                </ListItem>
                            ))}
                        </List>
                    </Box>
                    : <Typography align='center' variant="h6" component="div" mt={2}>No To-Dos</Typography>
                }
            </Grid >
        </Stack >
    )
}

export default ToDoList

How do I perform a GET request after the POST or PUT operations? Where should I then put the dispatch(getToDos(user))? The comments in my code show the results of the methods I've already tried

After reading through the Redux-Toolkit docs again and looking through other SO posts, I learned of the proper way to perform asyncThunk calls in series.

Instead of performing them in my slice, I moved the calls to my component and used Promises to execute them. In order to catch the error from the request and have access to it inside the component, you have to use Toolkit's rejectWithValue parameter inside the asyncThunk.

Here's an example:

export const loginUser = createAsyncThunk('user/loginUser', async (loginData, { rejectWithValue }) => {
    try {
        const response = await axios.post('/login', loginData);
        return response.data
    } catch (err) {
        if (!err.response) {
            throw err
        }
        return rejectWithValue(err.response.data)
    }
})

By adding { rejectWithValue } to the parameter and return rejectWithValue(err.response.data) , you can access the response in the component, like this:

    const handleSubmit = () => {
        if (loginData?.email && loginData?.password) {
            dispatch(loginUser(loginData))
                .unwrap()
                .then(() => {
                    // perform further asyncThunk dispatches here
                })
                .catch(err => {
                    console.log(err) // the return rejectWithValue(err.response.data) is sent here
                });
        }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM