Refactor "New/Edit list" page to avoid setting state in effect (#36753)

This commit is contained in:
diondiondion 2025-11-06 12:00:37 +01:00 committed by GitHub
parent 41a4022988
commit 58b3fc0379
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -22,6 +22,7 @@ import { Column } from 'mastodon/components/column';
import { ColumnHeader } from 'mastodon/components/column_header';
import { Icon } from 'mastodon/components/icon';
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
import type { List } from 'mastodon/models/list';
import { useAppDispatch, useAppSelector } from 'mastodon/store';
import { messages as membersMessages } from './members';
@ -72,36 +73,23 @@ const MembersLink: React.FC<{
);
};
const NewList: React.FC<{
multiColumn?: boolean;
}> = ({ multiColumn }) => {
const NewList: React.FC<{ list?: List | null }> = ({ list }) => {
const dispatch = useAppDispatch();
const { id } = useParams<{ id?: string }>();
const intl = useIntl();
const history = useHistory();
const list = useAppSelector((state) =>
id ? state.lists.get(id) : undefined,
);
const [title, setTitle] = useState('');
const [exclusive, setExclusive] = useState(false);
const [repliesPolicy, setRepliesPolicy] = useState<RepliesPolicyType>('list');
const {
id,
title: initialTitle = '',
exclusive: initialExclusive = false,
replies_policy: initialRepliesPolicy = 'list',
} = list ?? {};
const [title, setTitle] = useState(initialTitle);
const [exclusive, setExclusive] = useState(initialExclusive);
const [repliesPolicy, setRepliesPolicy] =
useState<RepliesPolicyType>(initialRepliesPolicy);
const [submitting, setSubmitting] = useState(false);
useEffect(() => {
if (id) {
dispatch(fetchList(id));
}
}, [dispatch, id]);
useEffect(() => {
if (id && list) {
setTitle(list.title);
setExclusive(list.exclusive);
setRepliesPolicy(list.replies_policy);
}
}, [setTitle, setExclusive, setRepliesPolicy, id, list]);
const handleTitleChange = useCallback(
({ target: { value } }: React.ChangeEvent<HTMLInputElement>) => {
setTitle(value);
@ -159,19 +147,6 @@ const NewList: React.FC<{
}, [history, dispatch, setSubmitting, id, title, exclusive, repliesPolicy]);
return (
<Column
bindToDocument={!multiColumn}
label={intl.formatMessage(id ? messages.edit : messages.create)}
>
<ColumnHeader
title={intl.formatMessage(id ? messages.edit : messages.create)}
icon='list-ul'
iconComponent={ListAltIcon}
multiColumn={multiColumn}
showBackButton
/>
<div className='scrollable'>
<form className='simple_form app-form' onSubmit={handleSubmit}>
<div className='fields-group'>
<div className='input with_label'>
@ -264,10 +239,7 @@ const NewList: React.FC<{
<div className='app-form__toggle__toggle'>
<div>
<Toggle
checked={exclusive}
onChange={handleExclusiveChange}
/>
<Toggle checked={exclusive} onChange={handleExclusiveChange} />
</div>
</div>
</label>
@ -285,6 +257,42 @@ const NewList: React.FC<{
</button>
</div>
</form>
);
};
const NewListWrapper: React.FC<{
multiColumn?: boolean;
}> = ({ multiColumn }) => {
const intl = useIntl();
const dispatch = useAppDispatch();
const { id } = useParams<{ id?: string }>();
const list = useAppSelector((state) =>
id ? state.lists.get(id) : undefined,
);
useEffect(() => {
if (id) {
dispatch(fetchList(id));
}
}, [dispatch, id]);
const isLoading = id && !list;
return (
<Column
bindToDocument={!multiColumn}
label={intl.formatMessage(id ? messages.edit : messages.create)}
>
<ColumnHeader
title={intl.formatMessage(id ? messages.edit : messages.create)}
icon='list-ul'
iconComponent={ListAltIcon}
multiColumn={multiColumn}
showBackButton
/>
<div className='scrollable'>
{isLoading ? <LoadingIndicator /> : <NewList list={list} />}
</div>
<Helmet>
@ -298,4 +306,4 @@ const NewList: React.FC<{
};
// eslint-disable-next-line import/no-default-export
export default NewList;
export default NewListWrapper;