most react hooks updated
This commit is contained in:
parent
434bc6bfe5
commit
e002b7092c
@ -6,12 +6,13 @@ module.exports = {
|
|||||||
plugins: [
|
plugins: [
|
||||||
'@typescript-eslint',
|
'@typescript-eslint',
|
||||||
'unused-imports',
|
'unused-imports',
|
||||||
|
'react',
|
||||||
|
'react-hooks',
|
||||||
],
|
],
|
||||||
extends: [
|
extends: [
|
||||||
'eslint:recommended',
|
'eslint:recommended',
|
||||||
'plugin:@typescript-eslint/recommended',
|
'plugin:@typescript-eslint/recommended',
|
||||||
'plugin:react/recommended',
|
'plugin:react/recommended',
|
||||||
'plugin:react-hooks/recommended',
|
|
||||||
],
|
],
|
||||||
settings: {
|
settings: {
|
||||||
react: {
|
react: {
|
||||||
@ -159,6 +160,9 @@ module.exports = {
|
|||||||
'react/jsx-props-no-multi-spaces': 'warn',
|
'react/jsx-props-no-multi-spaces': 'warn',
|
||||||
'react/jsx-props-no-spreading': 'error',
|
'react/jsx-props-no-spreading': 'error',
|
||||||
'react/jsx-tag-spacing': [ 'warn', { closingSlash: 'never', beforeSelfClosing: 'always', afterOpening: 'never', beforeClosing: 'never' } ],
|
'react/jsx-tag-spacing': [ 'warn', { closingSlash: 'never', beforeSelfClosing: 'always', afterOpening: 'never', beforeClosing: 'never' } ],
|
||||||
|
|
||||||
|
'react-hooks/rules-of-hooks': 'error',
|
||||||
|
'react-hooks/exhaustive-deps': 'warn',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ const DownloadButton: FC<DownloadButtonProps> = (props: DownloadButtonProps) =>
|
|||||||
|
|
||||||
setButtonText('Reveal in Explorer');
|
setButtonText('Reveal in Explorer');
|
||||||
setDownloading(false);
|
setDownloading(false);
|
||||||
}, [ downloading, buttonShaking, downloadPath, downloadBuff ]);
|
}, [ guild, resourceId, resourceName, downloading, buttonShaking, downloadPath, downloadBuff ]);
|
||||||
|
|
||||||
return <Button shaking={buttonShaking} onClick={downloadListener}>{buttonText}</Button>;
|
return <Button shaking={buttonShaking} onClick={downloadListener}>{buttonText}</Button>;
|
||||||
};
|
};
|
||||||
|
@ -18,7 +18,7 @@ const ChoicesControl: FC<ChoicesControlProps> = (props: ChoicesControlProps) =>
|
|||||||
>
|
>
|
||||||
{choice.display}
|
{choice.display}
|
||||||
</div>
|
</div>
|
||||||
)), [ choices, selectedId ]);
|
)), [ choices, selectedId, setSelectedId ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="choices-react">
|
<div className="choices-react">
|
||||||
|
@ -45,11 +45,11 @@ const Display: FC<DisplayProps> = (props: DisplayProps) => {
|
|||||||
}
|
}
|
||||||
}, [ saving, saveFailed ]);
|
}, [ saving, saveFailed ]);
|
||||||
|
|
||||||
const dismissInfoMessage = () => {
|
|
||||||
setDismissedInfoMessage(infoMessage);
|
|
||||||
};
|
|
||||||
|
|
||||||
const popup = useMemo(() => {
|
const popup = useMemo(() => {
|
||||||
|
const dismissInfoMessage = () => {
|
||||||
|
setDismissedInfoMessage(infoMessage);
|
||||||
|
};
|
||||||
|
|
||||||
if (errorMessage) {
|
if (errorMessage) {
|
||||||
return (
|
return (
|
||||||
<DisplayPopup tip={errorMessage}>
|
<DisplayPopup tip={errorMessage}>
|
||||||
@ -72,7 +72,7 @@ const Display: FC<DisplayProps> = (props: DisplayProps) => {
|
|||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}, [ errorMessage, changes, resetChanges, saveChanges ]);
|
}, [ errorMessage, infoMessage, dismissedInfoMessage, changes, resetChanges, saveChanges, saveButtonShaking, changesButtonText ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="display">
|
<div className="display">
|
||||||
|
@ -30,7 +30,7 @@ const DropdownInput: FC<DropdownInputProps> = (props: DropdownInputProps) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// only do this expensive version once
|
// only do this expensive version once
|
||||||
setDisplay(options.find(option => option.value === value)?.display ?? '');
|
setDisplay(options.find(option => option.value === value)?.display ?? '');
|
||||||
}, []);
|
}, [ options, value ]);
|
||||||
|
|
||||||
const optionElements = useMemo(() => options.map(option => {
|
const optionElements = useMemo(() => options.map(option => {
|
||||||
const className = option.value === value ? 'option selected' : 'option';
|
const className = option.value === value ? 'option selected' : 'option';
|
||||||
@ -40,7 +40,7 @@ const DropdownInput: FC<DropdownInputProps> = (props: DropdownInputProps) => {
|
|||||||
setOptionsOpen(false);
|
setOptionsOpen(false);
|
||||||
};
|
};
|
||||||
return <div className={className} key={option.value} onClick={onClick}>{option.display}</div>;
|
return <div className={className} key={option.value} onClick={onClick}>{option.display}</div>;
|
||||||
}), [ options ]);
|
}), [ options, setValue, value ]);
|
||||||
|
|
||||||
const labelElement = useMemo(() => label && <div className="label">{label}</div>, [ label ]);
|
const labelElement = useMemo(() => label && <div className="label">{label}</div>, [ label ]);
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ const TextInput: FC<TextInputProps> = React.forwardRef(function TextInput(props:
|
|||||||
}
|
}
|
||||||
setValid(true);
|
setValid(true);
|
||||||
setMessage(null);
|
setMessage(null);
|
||||||
}, [ value ]);
|
}, [ allowEmpty, label, maxLength, setMessage, setValid, value ]);
|
||||||
|
|
||||||
const labelElement = useMemo(() => label && <div className="label">{label}</div>, [ label ]);
|
const labelElement = useMemo(() => label && <div className="label">{label}</div>, [ label ]);
|
||||||
|
|
||||||
|
@ -18,6 +18,8 @@ const Overlay: FC<OverlayProps> = (props: OverlayProps) => {
|
|||||||
const setOverlay = useSetRecoilState<ReactNode>(overlayState);
|
const setOverlay = useSetRecoilState<ReactNode>(overlayState);
|
||||||
|
|
||||||
if (childRootRef) {
|
if (childRootRef) {
|
||||||
|
// this is alright to do since childRootRef (the ref itself) should never change for each component using this element
|
||||||
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
useActionWhenEscapeOrClickedOrContextOutsideEffect(childRootRef, () => setOverlay(null));
|
useActionWhenEscapeOrClickedOrContextOutsideEffect(childRootRef, () => setOverlay(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,12 +30,12 @@ const ConnectionInfoContextMenu: FC<ConnectionInfoContextMenuProps> = (props: Co
|
|||||||
<div className="status-circle" />
|
<div className="status-circle" />
|
||||||
<div className="status-text">{status}</div>
|
<div className="status-text">{status}</div>
|
||||||
</div>
|
</div>
|
||||||
)), [ setSelfStatus ]);
|
)), [ setSelfStatus, close ]);
|
||||||
|
|
||||||
const openPersonalize = useCallback(() => {
|
const openPersonalize = useCallback(() => {
|
||||||
close();
|
close();
|
||||||
setOverlay(<PersonalizeOverlay guild={guild} selfMember={selfMember} />);
|
setOverlay(<PersonalizeOverlay guild={guild} selfMember={selfMember} />);
|
||||||
}, [ close ]);
|
}, [ close, guild, selfMember, setOverlay ]);
|
||||||
|
|
||||||
const alignment = useMemo(() => ({ bottom: 'top', centerX: 'centerX' }), []);
|
const alignment = useMemo(() => ({ bottom: 'top', centerX: 'centerX' }), []);
|
||||||
|
|
||||||
@ -54,3 +54,4 @@ const ConnectionInfoContextMenu: FC<ConnectionInfoContextMenuProps> = (props: Co
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default ConnectionInfoContextMenu;
|
export default ConnectionInfoContextMenu;
|
||||||
|
|
||||||
|
@ -32,12 +32,12 @@ const GuildTitleContextMenu: FC<GuildTitleContextMenuProps> = (props: GuildTitle
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setOverlay(<GuildSettingsOverlay guild={guild} />);
|
setOverlay(<GuildSettingsOverlay guild={guild} />);
|
||||||
}, [ close ]);
|
}, [ close, guild, setOverlay ]);
|
||||||
|
|
||||||
const openCreateChannel = useCallback(() => {
|
const openCreateChannel = useCallback(() => {
|
||||||
close();
|
close();
|
||||||
setOverlay(<ChannelOverlay />);
|
setOverlay(<ChannelOverlay />);
|
||||||
}, [ close ]);
|
}, [ close, setOverlay ]);
|
||||||
|
|
||||||
const guildSettingsElement = useMemo(() => {
|
const guildSettingsElement = useMemo(() => {
|
||||||
if (!isLoaded(selfMember)) return null;
|
if (!isLoaded(selfMember)) return null;
|
||||||
@ -61,10 +61,11 @@ const GuildTitleContextMenu: FC<GuildTitleContextMenuProps> = (props: GuildTitle
|
|||||||
);
|
);
|
||||||
}, [ selfMember, openCreateChannel ]);
|
}, [ selfMember, openCreateChannel ]);
|
||||||
|
|
||||||
if (guildSettingsElement === null && createChannelElement === null) return null;
|
|
||||||
|
|
||||||
const alignment = useMemo(() => ({ top: 'bottom', centerX: 'centerX' }), []);
|
const alignment = useMemo(() => ({ top: 'bottom', centerX: 'centerX' }), []);
|
||||||
|
|
||||||
|
if (guildSettingsElement === null && createChannelElement === null) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ContextMenu alignment={alignment} close={close} relativeToRef={relativeToRef} realignDeps={[ selfMember ]}>
|
<ContextMenu alignment={alignment} close={close} relativeToRef={relativeToRef} realignDeps={[ selfMember ]}>
|
||||||
<div className="guild-title-context-menu">
|
<div className="guild-title-context-menu">
|
||||||
|
@ -43,14 +43,14 @@ const GuildOverviewDisplay: FC<GuildOverviewDisplayProps> = (props: GuildOvervie
|
|||||||
if (!isLoaded(guildMeta)) return;
|
if (!isLoaded(guildMeta)) return;
|
||||||
if (name === savedName) setName(guildMeta.value.name);
|
if (name === savedName) setName(guildMeta.value.name);
|
||||||
setSavedName(guildMeta.value.name);
|
setSavedName(guildMeta.value.name);
|
||||||
}, [ guildMeta ]);
|
}, [ guildMeta, name, savedName ]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isLoaded(iconResource)) {
|
if (isLoaded(iconResource)) {
|
||||||
if (iconBuff === savedIconBuff) setIconBuff(iconResource.value.data);
|
if (iconBuff === savedIconBuff) setIconBuff(iconResource.value.data);
|
||||||
setSavedIconBuff(iconResource.value.data);
|
setSavedIconBuff(iconResource.value.data);
|
||||||
}
|
}
|
||||||
}, [ iconResource ]);
|
}, [ iconBuff, iconResource, savedIconBuff ]);
|
||||||
|
|
||||||
const changes = useMemo(
|
const changes = useMemo(
|
||||||
() => name !== savedName || iconBuff?.toString('hex') !== savedIconBuff?.toString('hex'),
|
() => name !== savedName || iconBuff?.toString('hex') !== savedIconBuff?.toString('hex'),
|
||||||
@ -110,7 +110,7 @@ const GuildOverviewDisplay: FC<GuildOverviewDisplayProps> = (props: GuildOvervie
|
|||||||
}
|
}
|
||||||
|
|
||||||
setSaving(false);
|
setSaving(false);
|
||||||
}, [ name, iconBuff, errorMessage, saving, guild, iconBuff, savedIconBuff ]);
|
}, [ name, iconBuff, errorMessage, saving, savedName, savedIconBuff, guild ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Display
|
<Display
|
||||||
|
@ -50,7 +50,7 @@ const ChannelElement: FC<ChannelElementProps> = (props: ChannelElementProps) =>
|
|||||||
|
|
||||||
const launchModify = useCallback(() => {
|
const launchModify = useCallback(() => {
|
||||||
setOverlay(<ChannelOverlay channel={channel} />);
|
setOverlay(<ChannelOverlay channel={channel} />);
|
||||||
}, [ guild, channel ]);
|
}, [ setOverlay, channel ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={baseClassName} onClick={setSelfActiveChannel}>
|
<div className={baseClassName} onClick={setSelfActiveChannel}>
|
||||||
|
@ -47,7 +47,7 @@ const MessageList: FC = () => {
|
|||||||
result.push(<MessageElement key={guild.id + 'm#' + message.id} guild={guild} message={message} prevMessage={prevMessage} />);
|
result.push(<MessageElement key={guild.id + 'm#' + message.id} guild={guild} message={message} prevMessage={prevMessage} />);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}, [ messages ]);
|
}, [ guild, messages ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="message-list">
|
<div className="message-list">
|
||||||
|
@ -86,7 +86,7 @@ const AddGuildOverlay: FC<AddGuildOverlayProps> = (props: AddGuildOverlayProps)
|
|||||||
if (exampleAvatarBuff) {
|
if (exampleAvatarBuff) {
|
||||||
if (avatarBuff === null) setAvatarBuff(exampleAvatarBuff);
|
if (avatarBuff === null) setAvatarBuff(exampleAvatarBuff);
|
||||||
}
|
}
|
||||||
}, [ exampleAvatarBuff ]);
|
}, [ avatarBuff, exampleAvatarBuff ]);
|
||||||
|
|
||||||
const validationErrorMessage = useMemo(() => {
|
const validationErrorMessage = useMemo(() => {
|
||||||
if (exampleAvatarBuffError && !avatarBuff) return 'Unable to load example avatar';
|
if (exampleAvatarBuffError && !avatarBuff) return 'Unable to load example avatar';
|
||||||
|
@ -49,14 +49,14 @@ const ChannelOverlay: FC<ChannelOverlayProps> = (props: ChannelOverlayProps) =>
|
|||||||
} else {
|
} else {
|
||||||
setEdited(name.length > 0 && flavorText.length > 0);
|
setEdited(name.length > 0 && flavorText.length > 0);
|
||||||
}
|
}
|
||||||
}, [ name, flavorText ]);
|
}, [ name, flavorText, channel ]);
|
||||||
|
|
||||||
const validationErrorMessage = useMemo(() => {
|
const validationErrorMessage = useMemo(() => {
|
||||||
if (!edited) return null;
|
if (!edited) return null;
|
||||||
if (!nameInputValid && nameInputMessage) return nameInputMessage;
|
if (!nameInputValid && nameInputMessage) return nameInputMessage;
|
||||||
if (!flavorTextInputValid && flavorTextInputMessage) return flavorTextInputMessage;
|
if (!flavorTextInputValid && flavorTextInputMessage) return flavorTextInputMessage;
|
||||||
return null;
|
return null;
|
||||||
}, [ nameInputValid, nameInputMessage, flavorTextInputValid, flavorTextInputMessage ]);
|
}, [ edited, nameInputValid, nameInputMessage, flavorTextInputValid, flavorTextInputMessage ]);
|
||||||
|
|
||||||
const infoMessage = useMemo(() => {
|
const infoMessage = useMemo(() => {
|
||||||
if (nameInputValid && nameInputMessage) return nameInputMessage;
|
if (nameInputValid && nameInputMessage) return nameInputMessage;
|
||||||
|
@ -48,11 +48,11 @@ const PersonalizeOverlay: FC<PersonalizeOverlayProps> = (props: PersonalizeOverl
|
|||||||
if (avatarBuff === savedAvatarBuff) setAvatarBuff(avatarResource.value.data);
|
if (avatarBuff === savedAvatarBuff) setAvatarBuff(avatarResource.value.data);
|
||||||
setSavedAvatarBuff(avatarResource.value.data);
|
setSavedAvatarBuff(avatarResource.value.data);
|
||||||
}
|
}
|
||||||
}, [ avatarResource ]);
|
}, [ avatarBuff, avatarResource, savedAvatarBuff ]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
displayNameInputRef.current?.focus();
|
displayNameInputRef.current?.focus();
|
||||||
}, []);
|
}, [ displayNameInputRef ]);
|
||||||
|
|
||||||
const validationErrorMessage = useMemo(() => {
|
const validationErrorMessage = useMemo(() => {
|
||||||
if (isFailed(avatarResource)) return 'Unable to load avatar';
|
if (isFailed(avatarResource)) return 'Unable to load avatar';
|
||||||
|
@ -109,7 +109,7 @@ const SendMessage: FC<SendMessageProps> = (props: SendMessageProps) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, []);
|
}, [ isMounted ]);
|
||||||
|
|
||||||
const removeAttachment = useCallback(() => {
|
const removeAttachment = useCallback(() => {
|
||||||
setAttachmentBuff(null);
|
setAttachmentBuff(null);
|
||||||
@ -121,7 +121,7 @@ const SendMessage: FC<SendMessageProps> = (props: SendMessageProps) => {
|
|||||||
const attachmentPreview = useMemo(() => {
|
const attachmentPreview = useMemo(() => {
|
||||||
if (!attachmentBuff || !attachmentName) return null;
|
if (!attachmentBuff || !attachmentName) return null;
|
||||||
return <AttachmentPreview attachmentBuff={attachmentBuff} attachmentName={attachmentName} remove={removeAttachment} />;
|
return <AttachmentPreview attachmentBuff={attachmentBuff} attachmentName={attachmentName} remove={removeAttachment} />;
|
||||||
}, [ attachmentBuff, attachmentName ]);
|
}, [ attachmentBuff, attachmentName, removeAttachment ]);
|
||||||
|
|
||||||
// WARNING: The types on this are funky because of react's lack of explicit support for 'plaintext-only'
|
// WARNING: The types on this are funky because of react's lack of explicit support for 'plaintext-only'
|
||||||
const contentEditableType = useMemo(() => {
|
const contentEditableType = useMemo(() => {
|
||||||
|
Loading…
Reference in New Issue
Block a user