Skip to content

Commit

Permalink
perf: optimized state usage using useShallow and memoization
Browse files Browse the repository at this point in the history
  • Loading branch information
JairajJangle committed Aug 3, 2024
1 parent f78314d commit 0c8d9c6
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 40 deletions.
20 changes: 19 additions & 1 deletion src/TreeView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
} from './helpers';
import { useTreeViewStore } from './store/treeView.store';
import usePreviousState from './utils/usePreviousState';
import { useShallow } from "zustand/react/shallow";

const _TreeView = React.forwardRef<TreeViewRef, TreeViewProps>(
(props, ref) => {
Expand Down Expand Up @@ -59,7 +60,24 @@ const _TreeView = React.forwardRef<TreeViewRef, TreeViewProps>(
checked,

cleanUpTreeViewStore,
} = useTreeViewStore();
} = useTreeViewStore(useShallow(
state => ({
expanded: state.expanded,
updateExpanded: state.updateExpanded,

initialTreeViewData: state.initialTreeViewData,
updateInitialTreeViewData: state.updateInitialTreeViewData,

searchText: state.searchText,
updateSearchText: state.updateSearchText,

updateSearchKeys: state.updateSearchKeys,

checked: state.checked,

cleanUpTreeViewStore: state.cleanUpTreeViewStore,
})
));

React.useImperativeHandle(ref, () => ({
selectAll,
Expand Down
10 changes: 5 additions & 5 deletions src/components/CheckboxView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,27 +43,27 @@ function _CheckboxView(props: BuiltInCheckBoxViewProps) {
},
} = props;

function customCheckboxValueTypeToRNPaperType(
const customCheckboxValueTypeToRNPaperType = React.useCallback((
customCheckboxValueType: CheckboxValueType
) {
) => {
return customCheckboxValueType === 'indeterminate'
? 'indeterminate'
: customCheckboxValueType
? 'checked'
: 'unchecked';
}
}, []);

/**
* This function modifies the change in value when the previous state was indeterminate.
* If the prior value is 'indeterminate', it will mark the CheckBox as checked upon click.
*
* @param newValue This represents the updated CheckBox value after it's clicked.
*/
function onValueChangeModifier() {
const onValueChangeModifier = React.useCallback(() => {
// If the previous state was 'indeterminate', set checked to true
if (value === 'indeterminate') onValueChange(true);
else onValueChange(!value);
}
}, [onValueChange, value]);

return (
<View
Expand Down
65 changes: 31 additions & 34 deletions src/components/NodeList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import {
import { FlashList } from "@shopify/flash-list";

import type {
TreeNode,

CheckboxValueType,
__FlattenedTreeNode__,
NodeListProps,
Expand All @@ -27,6 +25,7 @@ import {
import { CheckboxView } from "./CheckboxView";
import { CustomExpandCollapseIcon } from "./CustomExpandCollapseIcon";
import { defaultIndentationMultiplier } from "../constants/treeView.constants";
import { useShallow } from 'zustand/react/shallow';

const NodeList = React.memo(_NodeList);
export default NodeList;
Expand All @@ -49,31 +48,28 @@ function _NodeList(props: NodeListProps) {
updateInnerMostChildrenIds,
searchKeys,
searchText
} = useTreeViewStore();

const [filteredTree, setFilteredTree] = React.useState<TreeNode[]>([]);
const [flattenedFilteredNodes, setFlattenedFilteredNodes]
= React.useState<__FlattenedTreeNode__[]>([]);
} = useTreeViewStore(useShallow(
state => ({
expanded: state.expanded,
initialTreeViewData: state.initialTreeViewData,
updateInnerMostChildrenIds: state.updateInnerMostChildrenIds,
searchKeys: state.searchKeys,
searchText: state.searchText,
})
));

// First we filter the tree as per the search term and keys
React.useEffect(() => {
const searchTrimmed = searchText.trim().toLowerCase();
const tempFilterTree = getFilteredTreeData(
initialTreeViewData,
searchTrimmed,
searchKeys
);
setFilteredTree(tempFilterTree);
}, [searchText, searchKeys, initialTreeViewData]);
const filteredTree = React.useMemo(() => getFilteredTreeData(
initialTreeViewData,
searchText.trim().toLowerCase(),
searchKeys
), [initialTreeViewData, searchText, searchKeys]);

// Then we flatten the treen to make it "render-compatible" in a "flat" list
React.useEffect(() => {
const tempFlattenTreeData = getFlattenedTreeData(
filteredTree,
expanded,
);
setFlattenedFilteredNodes(tempFlattenTreeData);
}, [filteredTree, expanded]);
const flattenedFilteredNodes = React.useMemo(() => getFlattenedTreeData(
filteredTree,
expanded,
), [filteredTree, expanded]);

// And update the innermost children id -> required to un/select filtered tree
React.useEffect(() => {
Expand Down Expand Up @@ -158,17 +154,18 @@ function _Node(props: NodeProps) {
CustomNodeRowComponent
} = props;

const { expanded, checked, indeterminate } = useTreeViewStore();

const isChecked = checked.has(node.id);
const isIndeterminate = indeterminate.has(node.id);
const isExpanded = expanded.has(node.id);

const [value, setValue] = React.useState(getValue(isChecked, isIndeterminate));

React.useEffect(() => {
setValue(getValue(isChecked, isIndeterminate));
}, [isChecked, isIndeterminate]);
const {
isExpanded,
value,
} = useTreeViewStore(useShallow(
state => ({
isExpanded: state.expanded.has(node.id),
value: getValue(
state.checked.has(node.id), // isChecked
state.indeterminate.has(node.id) // isIndeterminate
),
})
));

const _onToggleExpand = React.useCallback(() => {
handleToggleExpand(node.id);
Expand Down

0 comments on commit 0c8d9c6

Please sign in to comment.