React double rendering在react redux-toolkit的UI中显示数组中的每个对象2次

eyh26e7m  于 2023-10-19  发布在  React
关注(0)|答案(1)|浏览(83)

我用redux-toolkit做了一个公告板应用。我使用了jsonPlacehlder fake API作为应用程序的内容。但是从API获取数据后,在UI上显示数据时,每个对象显示2次。我从API获取的总数据是100。但由于这个问题,它显示200数据的UI上.每个物体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;
jum4pzuy

jum4pzuy1#

有几件事导致了重复状态:
1.该应用被渲染到一个React.StrictMode组件中,该组件在非生产构建中应用一些额外的逻辑,以帮助检测应用代码中的问题。在这种情况下,它是由修复双渲染发现的错误或修复重新运行效果发现的错误引起的。

  1. posts切片 always 将数据附加到posts状态数组。
    换句话说,获取posts数据的副作用是运行两次,发出两个API请求,第二个请求的数据附加到第一个请求的数据上。
    要解决此问题,您可以执行以下一项或多项操作:
  • 更新fetchPosts.fulfilled reducer case以替换posts状态,而不是追加到它。
.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操作上使用cancel/abort令牌,以便在组件卸载/装载时取消任何正在进行的API请求。请参阅Redux-Toolkit取消文档了解更多细节。

联系我们

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;
  }
});

Demo

相关问题