React, Redux:在导航失败后加载数据



现在我对react/redux很陌生,我想弄清楚,为什么我不能迭代一堆数据。这个应用程序非常简单:

我正在通过链接切换到一个表项页面->数据被加载并显示…到目前为止一切都很好。但是,当创建新条目并使用useNavigate切换回前页时,我得到以下错误:

Uncaught TypeError: books.books.map is not a function

booksSlice.js:

import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import axios from 'axios';
const BASE_URL = 'http://127.0.0.1:8000/api/book/';
const initialState = {
books: [],
status: 'idle', //'idle' | 'loading' | 'succeeded' | 'failed'
error: null
};
export const getBooks = createAsyncThunk('api/book', async (id=null, { rejectWithValue }) => {
try {
const response = await axios.get(BASE_URL);
return response.data.results;
} catch (err) {
console.log(err);
return rejectWithValue(err.response.data);
}
});
export const postBook = createAsyncThunk('api/book', async(book, { rejectWithValue }) => {
try {
const response = await axios.post(BASE_URL, book);
return response.data;
} catch (err) {
console.log(err);
return rejectWithValue(err.response.data);
}
});
const booksSlice = createSlice({
name: 'books',
initialState,
extraReducers(builder) {
builder
.addCase(getBooks.pending, (state, action) => {
state.status = 'loading';
})
.addCase(getBooks.fulfilled, (state, action) => {
state.books = action.payload;
state.status = 'succeeded';
})
.addCase(getBooks.rejected, (state, action) => {
state.status = 'failed';
state.error = action.error.message;
})
}
});
export const selectAllBooks = (state) => state.books;
export default booksSlice.reducer;

booksList.js:

import * as React from 'react';
import { Link } from 'react-router-dom';
import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { getBooks, selectAllBooks } from './booksSlice';
const BooksList = () => {
const dispatch = useDispatch();
const books = useSelector(selectAllBooks);

useEffect(() => {
dispatch(getBooks());
}, [dispatch]);
return(
<>
<Stack sx={{ maxWidth: "80%", mx: "auto", mt: "2rem" }} spacing={2} direction="row">
<Button variant="contained">
<Link className="link-create-book" to="create">Create Book</Link>
</Button>
</Stack>
<TableContainer sx={{ maxWidth: "80%", mx: "auto", mt: "2rem" }} component={Paper}>
<Table aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>Title</TableCell>
<TableCell align="right">Description</TableCell>
<TableCell align="right">Author</TableCell>
</TableRow>
</TableHead>
<TableBody>
{ books.books && books.books.map(item => (
<TableRow
key={item.id}
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
>
<TableCell component="th" scope="row">{item.title}</TableCell>
<TableCell align="right">{item.description.slice(0, 30)}...</TableCell>
<TableCell align="right">{item.full_author_name}</TableCell>
</TableRow>
)) }
</TableBody>
</Table>
</TableContainer>
</>
)
};
export default BooksList;

booksCreate.js:

import { useState } from "react";
import { useSelector, useDispatch } from 'react-redux';
import { useNavigate } from "react-router-dom";
import { postBook } from './booksSlice';
import { Link } from 'react-router-dom';
const BooksCreate = () => {
const dispatch = useDispatch();
const navigate = useNavigate();
const [title, setTitle] = useState('')
const [content, setContent] = useState('')
const [author, setAuthor] = useState('');
const onTitleChanged = (e) => setTitle(e.target.value);
const onContentChanged = (e) => setContent(e.target.value);
const onAuthorChanged = (e) => setAuthor(e.target.value);
const onSavePostClicked = () => {
try {
dispatch(postBook({                    
title,
description: content,
author,
})).unwrap();
setTitle('');
setContent('');
setAuthor('');
navigate('/book');
} catch (err) {
console.log('Failed to post new book!', err);
}
};
return(
<section className="create-book-section">
<h2>Add a new book</h2>
<form>
<label htmlFor="postTitle">Title:</label>
<input type="text" id="postTitle" name="postTitle" value={title} onChange={onTitleChanged} />
<label htmlFor="postDescription">Description:</label>
<textarea id="postDescription" name="postDescription" value={content} onChange={onContentChanged} />
<label htmlFor="postAuthor">Author:</label>
<input type="text" id="postAuthor" name="postAuthor" value={author} onChange={onAuthorChanged} />
<Stack sx={{ my: "1rem" }} spacing={2} direction="row">
<Button variant="contained" onClick={onSavePostClicked} disabled={!canSave} >Create Book</Button>
<Button variant="contained">
<Link className="link-create-book" to="/book">Back to books</Link>
</Button>
</Stack>
</form>
</section>
)
};
export default BooksCreate;

除此之外,我还试图安慰的反应。data显示错误消息后,我可以在控制台中看到新数据,尽管窗口中没有显示任何数据。如果您还需要看其他数据,请告诉我。

谢谢你的帮助,祝你有美好的一天!

在您的booksSlice.json createSlice中,添加此操作将您的状态重置为空[]

reducers: {
clearBooks(state){
state.books= [];
}
}
export const { 
clearBooks
} = booksSlice.actions;

然后在您的booksList.js上调用此阳离子以在组件卸载时清除状态,您可以添加额外的useEffect或编辑现有的

import { getBooks, selectAllBooks, clearBooks } from './booksSlice';
useEffect(() => {
dispatch(getBooks());
return () => {
dispatch(clearBooks())
}
}, [dispatch]);

Ok…因此,回顾一下,解决方案似乎很明显:我在一个extraReducer中犯了一个错误——当get方法失败时——并且忘记为post方法包括reducer !

.addCase(getBooks.rejected, (state, action) => {
state.books = [];
state.status = 'failed';
state.error = action.error.message;
})
.addCase(postBook.fulfilled, (state, action) => {
state.books.unshift(action.payload);
state.status = 'succeed';
})

最新更新