perf(homepage): isolate boards list from live hook churn

This commit is contained in:
Tommaso Casaburi
2026-04-15 14:36:18 +07:00
parent 7bf0888221
commit 7fb825b4c6
4 changed files with 37 additions and 35 deletions
+2 -2
View File
@@ -60,8 +60,8 @@ vi.mock('../boards-list', () => ({
}));
vi.mock('../popular-threads-box', () => ({
default: ({ directories, communities }: { directories: unknown[]; communities: Record<string, unknown> }) =>
createElement('div', { 'data-testid': 'popular-threads-box' }, `popular:${directories.length}:${Object.keys(communities).length}`),
default: ({ directories, directoryAddresses }: { directories: unknown[]; directoryAddresses: string[] }) =>
createElement('div', { 'data-testid': 'popular-threads-box' }, `popular:${directories.length}:${directoryAddresses.length}`),
}));
vi.mock('../../../components/site-legal-meta', () => ({
+29 -18
View File
@@ -1,7 +1,7 @@
import { Link, useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { useAccountCommunityAddresses } from '../../../hooks/use-account-community-addresses';
import { useDirectoriesState, useDirectories, DirectoryCommunity } from '../../../hooks/use-directories';
import { useDirectoriesState, DirectoryCommunity } from '../../../hooks/use-directories';
import { getBoardPath } from '../../../lib/utils/route-utils';
import useDisclaimerModalStore from '../../../stores/use-disclaimer-modal-store';
import useDirectoryModalStore from '../../../stores/use-directory-modal-store';
@@ -55,20 +55,37 @@ const BoardLink = ({ boardName, address, getBoardLink, onLinkClick, onPlaceholde
);
};
const DirectoriesErrorMessage = () => {
const error = useDirectoriesState().error;
return error?.message ? <div className='red'>{error.message}</div> : null;
};
const ModeratedBoardsLink = () => {
const { t } = useTranslation();
const accountCommunityAddresses = useAccountCommunityAddresses();
if (accountCommunityAddresses.length === 0) {
return null;
}
return (
<li>
<Link to='/mod'>{t('boards_you_moderate_nav')}</Link>
</li>
);
};
const BoardsList = ({ multisub }: { multisub: DirectoryCommunity[] }) => {
const { t } = useTranslation();
const navigate = useNavigate();
const { error } = useDirectoriesState();
const { showDisclaimerModal } = useDisclaimerModalStore();
const { openDirectoryModal } = useDirectoryModalStore();
const { useCatalogLinks, boardFilter } = useBoardsFilterStore();
const directories = useDirectories();
const accountCommunityAddresses = useAccountCommunityAddresses();
const showDisclaimerModal = useDisclaimerModalStore((state) => state.showDisclaimerModal);
const openDirectoryModal = useDirectoryModalStore((state) => state.openDirectoryModal);
const useCatalogLinks = useBoardsFilterStore((state) => state.useCatalogLinks);
const boardFilter = useBoardsFilterStore((state) => state.boardFilter);
const handleLinkClick = (e: React.MouseEvent<HTMLAnchorElement>, address: string) => {
e.preventDefault();
const boardPath = getBoardPath(address, directories);
const boardPath = getBoardPath(address, multisub);
const targetPath = boardPath + (useCatalogLinks ? '/catalog' : '');
showDisclaimerModal(address, navigate, targetPath);
};
@@ -76,7 +93,7 @@ const BoardsList = ({ multisub }: { multisub: DirectoryCommunity[] }) => {
// Helper to generate link URL with optional catalog suffix
const getBoardLink = (address: string | null): string => {
if (!address) return '#';
const boardPath = getBoardPath(address, directories);
const boardPath = getBoardPath(address, multisub);
return `/${boardPath}${useCatalogLinks ? '/catalog' : ''}`;
};
@@ -101,8 +118,6 @@ const BoardsList = ({ multisub }: { multisub: DirectoryCommunity[] }) => {
onPlaceholderClick: handlePlaceholderClick,
};
const errorMessage = error?.message;
// Filtering logic: determine which categories to show
const showAll = boardFilter === 'all';
const showNsfwOnly = boardFilter === 'nsfw';
@@ -125,7 +140,7 @@ const BoardsList = ({ multisub }: { multisub: DirectoryCommunity[] }) => {
<BoardsFilterModal />
</div>
<div className={`${styles.boxContent} ${styles.boardsContent}`}>
{errorMessage && <div className='red'>{errorMessage}</div>}
<DirectoriesErrorMessage />
{/* Column 1: Japanese Culture + Video Games */}
{(showJapaneseCulture || showVideoGames) && (
<div className={styles.boardsColumn}>
@@ -598,11 +613,7 @@ const BoardsList = ({ multisub }: { multisub: DirectoryCommunity[] }) => {
<li>
<Link to='/subs'>Subscriptions</Link>
</li>
{accountCommunityAddresses.length > 0 && (
<li>
<Link to='/mod'>{t('boards_you_moderate_nav')}</Link>
</li>
)}
<ModeratedBoardsLink />
</ul>
</div>
)}
+1 -5
View File
@@ -1,10 +1,8 @@
import { useEffect, useMemo, useRef, FormEvent } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { Trans, useTranslation } from 'react-i18next';
import { useCommunities } from '@bitsocialnet/bitsocial-react-hooks';
import styles from './home.module.css';
import { useDirectories, useDirectoryAddresses } from '../../hooks/use-directories';
import { useCommunityIdentifiers } from '../../hooks/use-community-identifiers';
import { CommunityStatsCollector, useCommunitiesStatsStore } from '../../hooks/use-communities-stats';
import PopularThreadsBox from './popular-threads-box';
import BoardsList from './boards-list';
@@ -190,8 +188,6 @@ export const HomeLogo = () => {
const Home = () => {
const directories = useDirectories();
const directoryAddresses = useDirectoryAddresses();
const directoryCommunities = useCommunityIdentifiers(directoryAddresses);
const { communities } = useCommunities({ communities: directoryCommunities });
const { closeDirectoryModal } = useDirectoryModalStore();
useEffect(() => {
@@ -214,7 +210,7 @@ const Home = () => {
<SearchBar />
<InfoBox />
<BoardsList multisub={directories} />
<PopularThreadsBox directories={directories} directoryAddresses={directoryAddresses} communities={communities} />
<PopularThreadsBox directories={directories} directoryAddresses={directoryAddresses} />
<Stats directoryAddresses={directoryAddresses} />
<Footer />
</div>
@@ -1,7 +1,7 @@
import { memo, useMemo } from 'react';
import { Link } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { Comment, Community } from '@bitsocialnet/bitsocial-react-hooks';
import { Comment, useCommunities } from '@bitsocialnet/bitsocial-react-hooks';
import styles from '../home.module.css';
import usePopularPosts from '../../../hooks/use-popular-posts';
import { useFeedStateString } from '../../../hooks/use-state-string';
@@ -11,6 +11,7 @@ import { CatalogPostMedia } from '../../../components/catalog-row';
import LoadingEllipsis from '../../../components/loading-ellipsis';
import BoxModal from '../box-modal';
import { DirectoryCommunity, findDirectoryByAddress } from '../../../hooks/use-directories';
import { useCommunityIdentifiers } from '../../../hooks/use-community-identifiers';
import { getBoardPath } from '../../../lib/utils/route-utils';
import { removeMarkdown } from '../../../lib/utils/post-utils';
@@ -55,18 +56,12 @@ const PopularThreadCard = memo(
(prevProps, nextProps) => prevProps.post?.cid === nextProps.post?.cid && prevProps.boardTitle === nextProps.boardTitle && prevProps.boardPath === nextProps.boardPath,
);
const PopularThreadsBox = ({
directories,
directoryAddresses,
communities,
}: {
directories: DirectoryCommunity[];
directoryAddresses: string[];
communities: Array<Community | undefined>;
}) => {
const PopularThreadsBox = ({ directories, directoryAddresses }: { directories: DirectoryCommunity[]; directoryAddresses: string[] }) => {
const { t } = useTranslation();
const { showWorksafeContentOnly, showNsfwContentOnly } = usePopularThreadsOptionsStore();
const getCommentCommunityAddress = (post: Comment) => post.communityAddress || post.subplebbitAddress;
const directoryCommunities = useCommunityIdentifiers(directoryAddresses);
const { communities } = useCommunities({ communities: directoryCommunities });
const { filteredBoardAddresses, filteredCommunities } = useMemo(() => {
const filteredEntries = directoryAddresses.flatMap((address, index) => {