我正在使用 redux-toolkit 构建一个公告板应用程序。我对应用程序的内容使用了 jsonPlacehlder fake API。但是从 API 获取数据后,在 UI 上显示数据时,每个对象都显示了 2 次。我从 API 获取的总数据是 100。但是由于这个问题,它在 UI 上显示 200 条数据。每个对象2次。下面给出了所有必要的代码。请帮忙解决这个问题。
来自 postSlice.js 的代码:
import { createSlice, nanoid,createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
import { sub } from "date-fns";
const POSTS_URL = 'http://jsonplaceholder.typicode.com/posts';
const initialState = {
posts: [],
status: 'idle',
error: null
}
export const fetchPosts = createAsyncThunk('posts/getPosts', async () => {
const response = await axios.get(POSTS_URL);
// console.log(response.data)
return response.data;
})
const postsSlice = createSlice({
name: 'posts',
initialState,
reducers: {
postAdded: {
reducer(state, action) {
state.posts.push(action.payload)
},
prepare(title, content, userId) {
return{
payload: {
id: nanoid(),
title,
content,
date: new Date().toISOString(),
userId,
reactions: {
like: 0,
love: 0,
wow: 0,
coffee: 0
}
}
}
}
},
addReactions(state, action) {
const { postId, reaction } = action.payload;
const postToReact = state.posts.find(post => post.id === postId);
if(postToReact){
postToReact.reactions[reaction]++
}
}
},
extraReducers(builder) {
builder
.addCase(fetchPosts.pending, (state, action) => {
state.status = 'loading'
})
.addCase(fetchPosts.fulfilled, (state, action) => {
state.status = 'succeeded';
// adding date and reactions because they are not available in the api data
let min = 1;
const loadedPosts = action.payload.map(post => {
post.date = sub(new Date(), {minutes: min++}).toISOString();
post.reactions = {
like: 0,
love: 0,
wow: 0,
coffee: 0
}
return post;
})
state.posts = state.posts.concat(loadedPosts);
})
.addCase(fetchPosts.rejected, (state, action) => {
state.status = 'failed';
state.error = action.error.message
})
}
});
export const selectAllPost = state => state.posts.posts;
export const getPostStatus = state => state.posts.status;
export const getPostError = state => state.posts.error;
export const { postAdded, addReactions } = postsSlice.actions
export default postsSlice.reducer;
来自 PostList.js 组件的代码,用于显示所有帖子:
import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import Post from './Post';
import { selectAllPost, getPostStatus, getPostError, fetchPosts } from '../../features/posts/postsSlice';
import { parseISO } from 'date-fns';
const PostsList = () => {
const dispatch = useDispatch()
const posts = useSelector(selectAllPost);
const postStatus = useSelector(getPostStatus);
const postError = useSelector(getPostError);
useEffect(() => {
if (postStatus === 'idle') {
dispatch(fetchPosts())
}
}, [postStatus, dispatch])
let content;
if(postStatus === 'loading'){
content = <span className="loading loading-bars loading-lg"></span>
} else if(postStatus === 'succeeded') {
const sortedPosts = posts.slice().sort((a, b) => parseISO(b.date) - parseISO(a.date));
content = sortedPosts.map((post, index) =>
<Post key={index} post={post} />
);
console.log(sortedPosts)
} else if(postStatus === 'failed') {
content = {postError}
}
return (
<div>
<h1 className='text-center text-2xl font-bold mb-4'>Posts</h1>
{content}
</div>
)
}
export default PostsList;
有几个因素会导致重复状态:
React.StrictMode
组件,该组件在非生产版本中应用一些附加逻辑,以帮助检测应用程序代码中的问题。在这种情况下,这是由修复双重渲染发现的错误或修复重新运行效果发现的错误引起的。换句话说,获取帖子数据的副作用是运行两次并发出两个 API 请求,第二个数据附加到第一个获取的数据。
要解决此问题,您可以执行以下一项或多项操作:
更新
fetchPosts.fulfilled
减速器案例以替换帖子状态而不是附加到它。
.addCase(fetchPosts.fulfilled, (state, action) => {
state.status = "succeeded";
// adding date and reactions because they are not available in the api data
let min = 1;
const loadedPosts = action.payload.map((post) => {
post.date = sub(new Date(), { minutes: min++ }).toISOString();
post.reactions = {
like: 0,
love: 0,
wow: 0,
coffee: 0
};
return post;
});
state.posts = loadedPosts; // <-- replace posts array completely
})
在
fetchPosts
操作上使用取消/中止令牌,以便在组件卸载/安装的情况下,任何正在进行的 API 请求都将被取消。有关更多详细信息,请参阅 Redux-Toolkit Cancellation 文档。
帖子列表
useEffect(() => {
const promise = dispatch(fetchPosts());
return () => {
promise?.abort();
};
}, []);
postSlice.js - 检查
fetchPosts
是否已中止,仅为未中止的 API 请求设置错误状态。
.addCase(fetchPosts.rejected, (state, action) => {
if (action.error.message !== "Aborted") {
state.status = "failed";
state.error = action.error.message;
}
});