pl-fe: limit rerenders

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
marcin mikołajczak 2024-10-25 23:38:31 +02:00
parent d3b5078030
commit 0a253894a6
5 changed files with 37 additions and 25 deletions

View file

@ -32,7 +32,7 @@ const useAccount = (accountId?: string, opts: UseAccountOpts = {}) => {
{ enabled: !!accountId, transform: normalizeAccount }, { enabled: !!accountId, transform: normalizeAccount },
); );
const meta = useAppSelector((state) => accountId && state.accounts_meta[accountId] || {}); const meta = useAppSelector((state) => accountId && state.accounts_meta[accountId]);
const { const {
relationship, relationship,

View file

@ -2,7 +2,7 @@ import React from 'react';
import { uploadCompose } from 'pl-fe/actions/compose'; import { uploadCompose } from 'pl-fe/actions/compose';
import { useAppDispatch } from 'pl-fe/hooks/useAppDispatch'; import { useAppDispatch } from 'pl-fe/hooks/useAppDispatch';
import { useAppSelector } from 'pl-fe/hooks/useAppSelector'; import { useCompose } from 'pl-fe/hooks/useCompose';
import UploadButton from '../components/upload-button'; import UploadButton from '../components/upload-button';
@ -14,10 +14,7 @@ interface IUploadButtonContainer {
const UploadButtonContainer: React.FC<IUploadButtonContainer> = ({ composeId }) => { const UploadButtonContainer: React.FC<IUploadButtonContainer> = ({ composeId }) => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const { disabled, resetFileKey } = useAppSelector((state) => ({ const { is_uploading: disabled, resetFileKey } = useCompose(composeId);
disabled: state.compose.get(composeId)?.is_uploading,
resetFileKey: state.compose.get(composeId)?.resetFileKey!,
}));
const onSelectFile = (files: FileList, intl: IntlShape) => { const onSelectFile = (files: FileList, intl: IntlShape) => {
dispatch(uploadCompose(composeId, files, intl)); dispatch(uploadCompose(composeId, files, intl));

View file

@ -3,14 +3,16 @@ import throttle from 'lodash/throttle';
import React, { useEffect, useMemo } from 'react'; import React, { useEffect, useMemo } from 'react';
import { defineMessages, useIntl } from 'react-intl'; import { defineMessages, useIntl } from 'react-intl';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { createSelector } from 'reselect';
import { fetchOwnAccounts, logOut, switchAccount } from 'pl-fe/actions/auth'; import { fetchOwnAccounts, logOut, switchAccount } from 'pl-fe/actions/auth';
import Account from 'pl-fe/components/account'; import Account from 'pl-fe/components/account';
import DropdownMenu from 'pl-fe/components/dropdown-menu'; import DropdownMenu from 'pl-fe/components/dropdown-menu';
import { Entities } from 'pl-fe/entity-store/entities';
import { useAppDispatch } from 'pl-fe/hooks/useAppDispatch'; import { useAppDispatch } from 'pl-fe/hooks/useAppDispatch';
import { useAppSelector } from 'pl-fe/hooks/useAppSelector'; import { useAppSelector } from 'pl-fe/hooks/useAppSelector';
import { useFeatures } from 'pl-fe/hooks/useFeatures'; import { useFeatures } from 'pl-fe/hooks/useFeatures';
import { makeGetAccount } from 'pl-fe/selectors'; import { RootState } from 'pl-fe/store';
import ThemeToggle from './theme-toggle'; import ThemeToggle from './theme-toggle';
@ -35,15 +37,18 @@ type IMenuItem = {
action?: (event: React.MouseEvent) => void; action?: (event: React.MouseEvent) => void;
} }
const getAccount = makeGetAccount();
const getOtherAccounts = createSelector([
(state: RootState) => state.auth.users,
(state: RootState) => state.entities[Entities.ACCOUNTS]?.store,
], (signedAccounts, accountEntities) => signedAccounts.toArray().map(([_, { id }]) => accountEntities?.[id] as AccountEntity).filter(account => account));
const ProfileDropdown: React.FC<IProfileDropdown> = ({ account, children }) => { const ProfileDropdown: React.FC<IProfileDropdown> = ({ account, children }) => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const features = useFeatures(); const features = useFeatures();
const intl = useIntl(); const intl = useIntl();
const authUsers = useAppSelector((state) => state.auth.users); const otherAccounts = useAppSelector(getOtherAccounts);
const otherAccounts = useAppSelector((state) => authUsers.map((authUser: any) => getAccount(state, authUser.id)!));
const handleLogOut = () => { const handleLogOut = () => {
dispatch(logOut()); dispatch(logOut());
@ -66,7 +71,7 @@ const ProfileDropdown: React.FC<IProfileDropdown> = ({ account, children }) => {
menu.push({ text: renderAccount(account), to: `/@${account.acct}` }); menu.push({ text: renderAccount(account), to: `/@${account.acct}` });
otherAccounts.forEach((otherAccount: AccountEntity) => { otherAccounts.forEach((otherAccount?: AccountEntity) => {
if (otherAccount && otherAccount.id !== account.id) { if (otherAccount && otherAccount.id !== account.id) {
menu.push({ menu.push({
text: renderAccount(otherAccount), text: renderAccount(otherAccount),
@ -99,11 +104,11 @@ const ProfileDropdown: React.FC<IProfileDropdown> = ({ account, children }) => {
))} ))}
</> </>
); );
}, [account, authUsers, features]); }, [account, otherAccounts.length, features]);
useEffect(() => { useEffect(() => {
fetchOwnAccountThrottled(); fetchOwnAccountThrottled();
}, [account.id, authUsers]); }, [account.id, otherAccounts.length]);
return ( return (
<DropdownMenu <DropdownMenu

View file

@ -1,6 +1,7 @@
import { QueryClientProvider } from '@tanstack/react-query'; import { QueryClientProvider } from '@tanstack/react-query';
import React from 'react'; import React from 'react';
import { HelmetProvider } from 'react-helmet-async'; import { HelmetProvider } from 'react-helmet-async';
import { Toaster } from 'react-hot-toast';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import { StatProvider } from 'pl-fe/contexts/stat-context'; import { StatProvider } from 'pl-fe/contexts/stat-context';
@ -26,18 +27,26 @@ store.dispatch(checkOnboardingStatus() as any);
/** The root React node of the application. */ /** The root React node of the application. */
const PlFe: React.FC = () => ( const PlFe: React.FC = () => (
<Provider store={store}> <>
<QueryClientProvider client={queryClient}> <Provider store={store}>
<StatProvider> <QueryClientProvider client={queryClient}>
<HelmetProvider> <StatProvider>
<PlFeHead /> <HelmetProvider>
<PlFeLoad> <PlFeHead />
<PlFeMount /> <PlFeLoad>
</PlFeLoad> <PlFeMount />
</HelmetProvider> </PlFeLoad>
</StatProvider> </HelmetProvider>
</QueryClientProvider> </StatProvider>
</Provider> </QueryClientProvider>
</Provider>
<div id='toaster'>
<Toaster
position='top-right'
containerClassName='top-4'
/>
</div>
</>
); );
export { PlFe as default }; export { PlFe as default };

View file

@ -349,6 +349,7 @@ const makeGetStatusIds = () => createSelector([
export { export {
type RemoteInstance, type RemoteInstance,
selectAccount, selectAccount,
selectAccounts,
selectOwnAccount, selectOwnAccount,
makeGetAccount, makeGetAccount,
getFilters, getFilters,