我正在开发一个新闻聚合器,我有一个 Newsfeed
组件可以映射相关帖子并为每个帖子创建一个 Post
组件。有一个 Sidebar
组件向用户显示他们订阅的提要,并允许他们订阅新提要或取消订阅现有提要。我希望发生的是:
Newsfeed
会重新呈现并显示来自新供稿的帖子。Newsfeed
会重新呈现并且不再显示来自该特定供稿的帖子。至于检索正确的帖子 - 我的后端负责处理,并且工作正常。后端根据用户订阅的提要返回帖子。问题是,当用户添加或删除提要时,Newsfeed
组件不会重新呈现,并且需要重新加载页面才能显示更新的提要。然而与此同时,Redux 商店也更新了,我每次都可以通过 Redux Dev Tools 看到状态变化。
在 Newsfeed
中,我使用 useSelector
钩子来获取几个不同的状态片段,但是当状态改变时组件不会重新渲染。我的印象是,任何使用 useSelector
钩子的组件都会在状态改变时自动重新渲染,但如果钩子不是这样工作的,请纠正我。
Newsfeed.tsx
:
import React, { useState, useRef, useCallback } from "react";
import usePostFetch from "../hooks/usePostFetch";
import { Post } from "./Post";
import { Tag } from "./Tag";
import { Upvote } from "./Upvote";
import { getDate } from "../services/getDate";
import { useSelector } from "react-redux";
import { InitialState } from "../store/reducers/rootReducer";
export const Newsfeed = (props: any) => {
const userState = useSelector((state: InitialState) => {
return state.auth;
});
const { user } = userState;
const publisherState = useSelector((state: InitialState) => {
return state.publishers.publishers;
});
const [pageNumber, setPageNumber] = useState(1);
const { loading, error, posts, hasMore } = usePostFetch(pageNumber);
const observer: any = useRef();
const lastPostElementRef = useCallback(
(node) => {
if (loading) return;
if (observer && observer.current) observer.current.disconnect();
observer.current = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting && hasMore) {
console.log("Visible ");
setPageNumber((prevPageNumber) => prevPageNumber + 1);
}
});
if (node) observer.current.observe(node);
console.log(node);
},
[loading, hasMore]
);
return (
<div className="container mx-auto bg-gray-900" id="newsfeed">
<div className="object-center grid grid-cols-1 gap-8 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 mx-auto pb-6 pt-6">
{posts.map((post, index) => {
return (
<Post
key={post.id}
title={post.title}
url={post.url}
image={post.image}
category={post.category}
postId={post._id}
created={post.created}
publisher={post.publisher}
upvotes={post.upvotes}
/>
);
})}
</div>
<div>{loading && "Loading..."}</div>
<div>{error && "Error"}</div>
</div>
);
};
publisherActions.ts
:(相关部分)
export const removeFeed = (allFeeds: any, feedName: any, userId: any) => async (
dispatch: Dispatch<PublisherDispatchTypes>
) => {
try {
axios({
method: "PUT",
url: "users/removepublisher",
params: { publisher: feedName, userId },
})
.then((res) => {
let newAllFeeds = allFeeds.filter((feed: any) => {
return feed.name.localeCompare(feedName) !== 0;
});
allFeeds = newAllFeeds;
console.log(`Feed was removed, ${res}`);
dispatch({
type: REMOVE_FEED_SUCCESS,
payload: allFeeds,
});
})
.catch((err) => {
console.log(`Error removing feed, ${err}`);
dispatch({
type: REMOVE_FEED_FAILURE,
});
});
} catch {
dispatch({ type: REMOVE_FEED_FAILURE });
console.log("Caught error while removing feed");
}
};
export const addFeed = (allFeeds: any, feed: any, userId: any) => async (
dispatch: Dispatch<PublisherDispatchTypes>
) => {
console.log("IN THE ADD_FEED FUNCTION");
try {
axios({
method: "PUT",
url: "users/addpublisher",
params: { publisher: feed, userId },
})
.then((res) => {
console.log(`Feed was added, ${res}`);
dispatch({
type: ADD_FEED_SUCCESS,
payload: {
name: feed.name,
url: feed.url,
image: feed.image,
},
});
})
.catch((err) => {
console.log(`Error adding feed, ${err}`);
dispatch({
type: ADD_FEED_FAILURE,
});
});
} catch {
dispatch({ type: ADD_FEED_FAILURE });
console.log("Caught error while adding feed");
}
publisherReducer.ts
:(相关部分)
import { Reducer } from "react";
import {
PublisherDispatchTypes,
REMOVE_FEED_SUCCESS,
REMOVE_FEED_FAILURE,
ADD_FEED_SUCCESS,
ADD_FEED_FAILURE,
} from "../actions/publisherActionsTypes";
import { Publisher } from "../../../../shared/Publisher";
interface PublisherResponse {
publishers: Publisher[];
}
export interface PublisherState {
publishers: Publisher[] | undefined;
loadedUsersFeeds: boolean;
feedCount: number;
}
const defaultState: PublisherState = {
publishers: undefined,
loadedUsersFeeds: false,
feedCount: 0,
};
const publisherReducer = (
state: PublisherState = defaultState,
action: PublisherDispatchTypes
) => {
switch (action.type) {
case REMOVE_FEED_SUCCESS:
return {
...state,
publishers: action.payload,
};
case REMOVE_FEED_FAILURE:
return state;
case ADD_FEED_SUCCESS:
let pubs = state.publishers || [];
return {
...state,
publishers: [...pubs, action.payload],
};
case ADD_FEED_FAILURE:
return state;
default:
return state;
}
};
export default publisherReducer;