Skip to content

Commit

Permalink
fix(@clayui/core): fixes error when deselecting the indeterminate sta…
Browse files Browse the repository at this point in the history
…te for pre selected items in the first render

This behavior should only happen when items change, as in React.js, regardless of the first render it will invoke useEffect, in this scenario this ends up breaking the hydrate behavior of the first render for the selected items.

So we prevent this case from being computed on the first rendering and only being called when the items change.
  • Loading branch information
matuzalemsteles committed Nov 27, 2023
1 parent ad2dff6 commit 997461e
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 3 deletions.
1 change: 1 addition & 0 deletions packages/clay-core/src/hooks/index.ts
Expand Up @@ -4,4 +4,5 @@
*/

export {useIsMounted} from './useIsMounted';
export {useIsFirstRender} from './useIsFirstRender';
export {useForwardRef} from './useForwardRef';
18 changes: 18 additions & 0 deletions packages/clay-core/src/hooks/useIsFirstRender.ts
@@ -0,0 +1,18 @@
/**
* SPDX-FileCopyrightText: © 2023 Liferay, Inc. <https://liferay.com>
* SPDX-License-Identifier: BSD-3-Clause
*/

import {useRef} from 'react';

export function useIsFirstRender(): boolean {
const isFirst = useRef(true);

if (isFirst.current) {
isFirst.current = false;

return true;
}

return isFirst.current;
}
14 changes: 11 additions & 3 deletions packages/clay-core/src/tree-view/useMultipleSelection.tsx
Expand Up @@ -7,6 +7,7 @@ import {useControlledState} from '@clayui/shared';
import {Key, useCallback, useEffect, useMemo, useRef} from 'react';

import {getKey} from '../collection';
import {useIsFirstRender} from '../hooks';
import {ITreeProps, createImmutableTree} from './useTree';

import type {ICollectionProps} from './Collection';
Expand Down Expand Up @@ -125,6 +126,8 @@ export function useMultipleSelection<T extends Record<string, any>>(
value: props.selectedKeys,
});

const isFirstRender = useIsFirstRender();

/**
* We are using `useMemo` to do indeterminate state revalidation in the
* render cycle instead of in the `useEffect` which happens after rendering.
Expand Down Expand Up @@ -170,11 +173,16 @@ export function useMultipleSelection<T extends Record<string, any>>(
}, [selectedKeys]);

/**
* This useEffect causes useMemo to be called every time the items change which can cause performance issues. We cannot change the useMemo because it is needed in other treeview cases. This should be improved in the future.
* This useEffect causes useMemo to be called every time the items change which
* can cause performance issues. We cannot change the useMemo because it is
* needed in other treeview cases. This should be improved in the future.
*/

useEffect(() => {
if (props.selectionMode === 'multiple-recursive' && !isUncontrolled) {
if (
!isFirstRender &&
props.selectionMode === 'multiple-recursive' &&
!isUncontrolled
) {
const newSelectedKeys = new Set(selectedKeys);

props.layoutKeys.current.forEach((keyMap, key) => {
Expand Down

0 comments on commit 997461e

Please sign in to comment.