Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Linting] Fixed useExhaustiveDependencies in biome #3197

Merged
merged 15 commits into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/bright-meals-repair.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@navikt/ds-react": patch
---

Performance: :zap: Optimized memoization for rerendring in some components.
KenAJoh marked this conversation as resolved.
Show resolved Hide resolved
KenAJoh marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 1 addition & 1 deletion @navikt/aksel-icons/figma-plugin/src/ui/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ const App = () => {
icon.removeEventListener("dragend", dragEvent);
}
};
}, [categories]);
}, []);

return (
<main tabIndex={-1} className="wrapper">
Expand Down
1 change: 0 additions & 1 deletion @navikt/core/css/copybutton.css
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
@media (forced-colors: active) {
.navds-copybutton {
background-color: ButtonFace;
border-color: ButtonText;
border: solid 1px ButtonText;
color: ButtonText;
}
Expand Down
1 change: 0 additions & 1 deletion @navikt/core/css/internalheader.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
@media (forced-colors: active) {
.navds-internalheader {
background-color: ButtonFace;
border-color: ButtonText;
border: solid 1px ButtonText;
color: ButtonText;
}
Expand Down
1 change: 0 additions & 1 deletion @navikt/core/css/read-more.css
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
@media (forced-colors: active) {
.navds-read-more__button {
background-color: ButtonFace;
border-color: ButtonText;
border: solid 1px ButtonText;
color: ButtonText;
}
Expand Down
65 changes: 31 additions & 34 deletions @navikt/core/react/src/date/datepicker/datepicker.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,40 +73,37 @@ export const Default: StoryObj<DefaultStoryProps> = {
locale={props.locale}
disableWeekends={props.disableWeekends}
>
{!props.standalone && (
<>
{props.inputfield && props.mode !== "multiple" ? (
<>
{props.mode === "range" ? (
<div style={{ display: "flex", gap: "1rem" }}>
<DatePicker.Input
label="Fra"
size={props?.size}
{...rangeCtx.fromInputProps}
/>
<DatePicker.Input
label="Til"
size={props?.size}
{...rangeCtx.toInputProps}
/>
</div>
) : (
<>
<DatePicker.Input
label="Velg dato"
size={props?.size}
{...singleCtx.inputProps}
/>
</>
)}
</>
) : (
<Button onClick={() => setOpen((x) => !x)}>
Åpne datovelger
</Button>
)}
</>
)}
{!props.standalone &&
(props.inputfield && props.mode !== "multiple" ? (
<>
{props.mode === "range" ? (
<div style={{ display: "flex", gap: "1rem" }}>
<DatePicker.Input
label="Fra"
size={props?.size}
{...rangeCtx.fromInputProps}
/>
<DatePicker.Input
label="Til"
size={props?.size}
{...rangeCtx.toInputProps}
/>
</div>
) : (
<>
<DatePicker.Input
label="Velg dato"
size={props?.size}
{...singleCtx.inputProps}
/>
</>
)}
</>
) : (
<Button onClick={() => setOpen((x) => !x)}>
Åpne datovelger
</Button>
))}
</Comp>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const FilteredOptions = () => {
)}

{shouldRenderFilteredOptionsList && (
/* biome-ignore lint/a11y/useFocusableInteractive: Interaction is not handeled by listbox itself. */
<ul
ref={setFilteredOptionsRef}
role="listbox"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ const InputProvider = ({ children, value: props }: Props) => {
setInternalValue("");
setSearchTerm("");
},
[externalOnChange, onClear, setInternalValue],
[externalOnChange, onClear],
);

const focusInput = useCallback(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,17 @@ const SelectedOptionsProvider = ({
[customOptions, onToggleSelected, removeCustomOption],
);

const isLimitReached =
(!!maxSelected?.limit && selectedOptions.length >= maxSelected.limit) ||
(!isMultiSelect && selectedOptions.length > 0);
const isLimitReached = useMemo(() => {
return (
(!!maxSelected?.limit && selectedOptions.length >= maxSelected.limit) ||
(!isMultiSelect && selectedOptions.length > 0)
);
}, [maxSelected, selectedOptions, isMultiSelect]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since there's no expensive calculations here, I don't think useMemo will have any benefits in this case.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its memozied to avoid this useEffect from running all the time:

useEffect(() => {
    setHideCaret(isLimitReached);
  }, [isLimitReached, setHideCaret]);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since isLimitReached is a boolean, it will not change on each render, as opposed to an object.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧠 🧠 🧠 🧠 🧠 🧠 🧠 TIL! So used to using bools in states like const [flag, setFlag]= useState.. where changing flag to the same value will cause useEffect from running that i didn't even know this


// biome-ignore lint/correctness/useExhaustiveDependencies: We explicitly want to run this effect when selectedOptions changes to match the view with the selected options.
useEffect(() => {
setHideCaret(isLimitReached);
}, [selectedOptions, setHideCaret, isLimitReached]);
}, [isLimitReached, selectedOptions, setHideCaret]);

const toggleOption = useCallback(
(
Expand Down
4 changes: 2 additions & 2 deletions @navikt/core/react/src/form/combobox/customOptionsContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const CustomOptionsProvider = ({
);
focusInput();
},
[focusInput, setCustomOptions],
[focusInput],
);

const addCustomOption = useCallback(
Expand All @@ -47,7 +47,7 @@ const CustomOptionsProvider = ({
}
focusInput();
},
[focusInput, isMultiSelect, setCustomOptions],
[focusInput, isMultiSelect],
);

const customOptionsState = {
Expand Down
4 changes: 2 additions & 2 deletions @navikt/core/react/src/modal/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export const Modal = forwardRef<HTMLDialogElement, ModalProps>(
// We have to use JS because it doesn't work to set it with a prop (React bug?)
// Currently doesn't seem to work in Chrome. See also Tooltip.tsx
if (modalRef.current && portalNode) modalRef.current.autofocus = true;
}, [modalRef, portalNode]);
}, [portalNode]);

useEffect(() => {
// We need to have this in a useEffect so that the content renders before the modal is displayed,
Expand All @@ -140,7 +140,7 @@ export const Modal = forwardRef<HTMLDialogElement, ModalProps>(
modalRef.current.close();
}
}
}, [modalRef, portalNode, open]);
}, [portalNode, open]);

useBodyScrollLock(modalRef, portalNode, isNested);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,8 @@ const DismissableLayerNode = forwardRef<HTMLDivElement, DismissableLayerProps>(
* If `disableOutsidePointerEvents` is true,
* we want to disable pointer events on the body when the first layer is opened.
*/

// biome-ignore lint/correctness/useExhaustiveDependencies: Every time the descendants change, we want to update the body pointer events since we might have added or removed a layer.
useEffect(() => {
if (!node || !enabled || !disableOutsidePointerEvents) return;

Expand All @@ -362,6 +364,7 @@ const DismissableLayerNode = forwardRef<HTMLDivElement, DismissableLayerProps>(
/**
* To make sure pointerEvents are enabled for all parents and siblings when the layer is removed from the DOM
*/
// biome-ignore lint/correctness/useExhaustiveDependencies: We explicitly want to run this on unmount, including every time the node updates to make sure we don't lock the application behind pointer-events: none.
useEffect(() => {
return () => descendants.values().forEach((x) => x.forceUpdate());
}, [descendants, node]);
Expand Down
1 change: 1 addition & 0 deletions @navikt/core/react/src/progress-bar/ProgressBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ export const ProgressBar = forwardRef<HTMLDivElement, ProgressBarProps>(
}, [simulated?.seconds]);

return (
/* biome-ignore lint/a11y/useFocusableInteractive: Progressbar is not interactive. */
<div
ref={ref}
className={cl(
Expand Down
31 changes: 15 additions & 16 deletions @navikt/core/react/src/table/AnimateHeight.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,37 +74,36 @@ const AnimateHeight: React.FC<AnimateHeightProps> = ({
const animationClassesTimeoutID = useRef<Timeout>();
const timeoutID = useRef<Timeout>();

const duration = prefersReducedMotion ? 0 : userDuration;
const initialHeight = useRef<Height>(height);
const initialOverflow = useRef<Overflow>("visible");

let initHeight: Height = height;
let initOverflow: Overflow = "visible";
const duration = prefersReducedMotion ? 0 : userDuration;

if (typeof initHeight === "number") {
if (typeof initialHeight.current === "number") {
// Reset negative height to 0
if (typeof height !== "string") {
initHeight = height < 0 ? 0 : height;
initialHeight.current = height < 0 ? 0 : height;
}
initOverflow = "hidden";
} else if (isPercentage(initHeight)) {
initialOverflow.current = "hidden";
} else if (isPercentage(initialHeight.current)) {
// If value is string "0%" make sure we convert it to number 0
initHeight = height === "0%" ? 0 : height;
initOverflow = "hidden";
initialHeight.current = height === "0%" ? 0 : height;
initialOverflow.current = "hidden";
}

const [currentHeight, setCurrentHeight] = useState<Height>(initHeight);
const [overflow, setOverflow] = useState<Overflow>(initOverflow);
const [currentHeight, setCurrentHeight] = useState<Height>(
initialHeight.current,
);
const [overflow, setOverflow] = useState<Overflow>(initialOverflow.current);
const [useTransitions, setUseTransitions] = useState<boolean>(false);

// ------------------ Did mount
useEffect(() => {
// Hide content if height is 0 (to prevent tabbing into it)
hideContent(contentElement.current, currentHeight);

// This should be explicitly run only on mount
// eslint-disable-next-line react-hooks/exhaustive-deps
hideContent(contentElement.current, initialHeight.current);
}, []);

// ------------------ Height update
// biome-ignore lint/correctness/useExhaustiveDependencies: This should be explicitly run only on height change
useEffect(() => {
if (height !== prevHeight.current && contentElement.current) {
showContent(contentElement.current, prevHeight.current);
Expand Down
59 changes: 0 additions & 59 deletions @navikt/core/react/src/table/stories/table-1.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,61 +98,6 @@ export const SizeSmall = () => <TableComponent size="small" />;

export const Buttons = () => <TableComponent size="small" button />;

export const WithDivs = () => {
return (
<div className="navds-table" role="table">
<div className="navds-table__header" role="rowgroup">
<div className="navds-table__row" role="row">
<div className="navds-table__header-cell" role="columnheader">
Fornavn
</div>
<div className="navds-table__header-cell" role="columnheader">
Etternavn
</div>
<div className="navds-table__header-cell" role="columnheader">
Rolle
</div>
</div>
</div>
<div className="navds-table__body" role="rowgroup">
<div className="navds-table__row" role="row">
<div className="navds-table__data-cell" role="cell">
Jean-Luc
</div>
<div className="navds-table__data-cell" role="cell">
Picard
</div>
<div className="navds-table__data-cell" role="cell">
Kaptein
</div>
</div>
<div className="navds-table__row" role="row">
<div className="navds-table__data-cell" role="cell">
William
</div>
<div className="navds-table__data-cell" role="cell">
Riker
</div>
<div className="navds-table__data-cell" role="cell">
Kommandør
</div>
</div>
<div className="navds-table__row" role="row">
<div className="navds-table__data-cell" role="cell">
Geordi
</div>
<div className="navds-table__data-cell" role="cell">
La Forge
</div>
<div className="navds-table__data-cell" role="cell">
Sjefsingeniør
</div>
</div>
</div>
</div>
);
};

const SelectionTable = ({ size = "medium" }: { size?: "small" | "medium" }) => {
const useToggleList = (initialState) => {
const [list, setList] = useState(initialState);
Expand Down Expand Up @@ -259,10 +204,6 @@ export const Chromatic = {
<h3>With Buttons</h3>
<Buttons />
</div>
<div>
<h3>Custom with divs</h3>
<WithDivs />
</div>
<div>
<h3>Selection</h3>
<Selection />
Expand Down
1 change: 1 addition & 0 deletions @navikt/core/react/src/util/TextareaAutoSize.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ const TextareaAutosize = forwardRef<HTMLTextAreaElement, TextareaAutosizeProps>(
syncHeight();
});

// biome-ignore lint/correctness/useExhaustiveDependencies: Since value is an external prop, we want to reset the renders on every time it changes, even when it is undefined or empty.
useEffect(() => {
renders.current = 0;
}, [value]);
Expand Down
5 changes: 3 additions & 2 deletions @navikt/core/react/src/util/create-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ export function createContext<T>(options: CreateContextOptions<T> = {}) {
const Provider = forwardRef<unknown, ProviderProps<T>>(
({ children, ...context }, ref) => {
// Only re-memoize when prop values change
// eslint-disable-next-line react-hooks/exhaustive-deps
const value = React.useMemo(() => context, Object.values(context)) as T;

// biome-ignore lint/correctness/useExhaustiveDependencies: Object.values(context) includes all dependencies.
const value = React.useMemo(() => context, Object.values(context)) as T; // eslint-disable-line react-hooks/exhaustive-deps

return (
<Context.Provider value={ref ? { ...value, ref } : value}>
Expand Down
Loading