Skip to content

feat(DualListSelector): add composable tree example#6652

Merged
tlabaj merged 6 commits intopatternfly:mainfrom
nicolethoen:dual_list_clean_up
Dec 8, 2021
Merged

feat(DualListSelector): add composable tree example#6652
tlabaj merged 6 commits intopatternfly:mainfrom
nicolethoen:dual_list_clean_up

Conversation

@nicolethoen
Copy link
Copy Markdown
Contributor

What: Closes #6401, Closes #6424

@patternfly-build
Copy link
Copy Markdown
Collaborator

patternfly-build commented Nov 29, 2021

@nicolethoen nicolethoen marked this pull request as ready for review December 2, 2021 16:56
@nicolethoen nicolethoen requested a review from kmcfaul December 2, 2021 16:57
filterValue && descendentsOnThisPane.some(id => memoizedNodeText[id].includes(filterValue));
const isFilterMatch = filterValue && node.text.includes(filterValue) && descendentsOnThisPane.length > 0;

// A node is displayed if:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit. can you update comment to say if either is true just to be more clear

Comment on lines +78 to +92
const { memoizedLeavesById, memoizedAllLeaves, memoizedNodeText } = React.useMemo(() => {
let leavesById = {};
let allLeaves = [];
let nodeTexts = {};
data.forEach(foodNode => {
nodeTexts = { ...nodeTexts, ...buildTextById(foodNode) };
leavesById = { ...leavesById, ...getLeavesById(foodNode) };
allLeaves = [...allLeaves, ...getDescendantLeafIds(foodNode)];
});
return {
memoizedLeavesById: leavesById,
memoizedAllLeaves: allLeaves,
memoizedNodeText: nodeTexts
};
}, [data]);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Shouldn't this be in the form of:

  const memoizeNodes = (data: FoodNode[]) => {
    let leavesById = {};
    let allLeaves = [];
    let nodeTexts = {};
    data.forEach(foodNode => {
      nodeTexts = { ...nodeTexts, ...buildTextById(foodNode) };
      leavesById = { ...leavesById, ...getLeavesById(foodNode) };
      allLeaves = [...allLeaves, ...getDescendantLeafIds(foodNode)];
    });
    return {
      memoizedLeavesById: leavesById,
      memoizedAllLeaves: allLeaves,
      memoizedNodeText: nodeTexts
    };
  };

  // Builds a map of child leaf nodes by node id - memoized so that it only rebuilds the list if the data changes.
  const { memoizedLeavesById, memoizedAllLeaves, memoizedNodeText } = React.useMemo(() => memoizeNodes(data), [data]);

with data being passed as an argument to the memoization callback?

This is a genuine question as I'm not well versed with React memoization and I'm not sure how to really test this without a large dataset, I just saw that in the React docs they show usage as:

image

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

My understanding is that React.useMemo works like React.useEffect where it watches for changes in the dependencies listed in the dependency array at the end - and it only recalculates the value of the memoizedValue if those dependencies change.

Copy link
Copy Markdown
Collaborator

@wise-king-sullyman wise-king-sullyman Dec 7, 2021

Choose a reason for hiding this comment

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

I misunderstood something from the docs and confused myself, disregard 😅

Copy link
Copy Markdown
Collaborator

@mturley mturley Dec 7, 2021

Choose a reason for hiding this comment

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

@wise-king-sullyman I think both are correct. Defining a dedicated memoizeNodes function at render time just before passing useMemo an anonymous function that calls it behaves the same as defining that behavior directly in the anonymous function. The important part is the [data] dependency array which will prevent the whole anonymous function from being run again if the data didn't change. So it's a matter of preference - I think the example in the React docs was written that way for brevity.

(edit: sorry, i didn't see your last comment before writing this!)

const [chosenLeafIds, setChosenLeafIds] = React.useState<string[]>(['beans', 'beef', 'chicken', 'tofu']);
const [chosenFilter, setChosenFilter] = React.useState<string>('');
const [availableFilter, setAvailableFilter] = React.useState<string>('');
let hiddenChosen = [] as string[];
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Nit, but I think you can do let hiddenChosen: string[] = []; to avoid the use of the as keyword (I often search for that and try to remove it since it can be a code smell, even though it isn't here)

Comment on lines +78 to +92
const { memoizedLeavesById, memoizedAllLeaves, memoizedNodeText } = React.useMemo(() => {
let leavesById = {};
let allLeaves = [];
let nodeTexts = {};
data.forEach(foodNode => {
nodeTexts = { ...nodeTexts, ...buildTextById(foodNode) };
leavesById = { ...leavesById, ...getLeavesById(foodNode) };
allLeaves = [...allLeaves, ...getDescendantLeafIds(foodNode)];
});
return {
memoizedLeavesById: leavesById,
memoizedAllLeaves: allLeaves,
memoizedNodeText: nodeTexts
};
}, [data]);
Copy link
Copy Markdown
Collaborator

@mturley mturley Dec 7, 2021

Choose a reason for hiding this comment

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

@wise-king-sullyman I think both are correct. Defining a dedicated memoizeNodes function at render time just before passing useMemo an anonymous function that calls it behaves the same as defining that behavior directly in the anonymous function. The important part is the [data] dependency array which will prevent the whole anonymous function from being run again if the data didn't change. So it's a matter of preference - I think the example in the React docs was written that way for brevity.

(edit: sorry, i didn't see your last comment before writing this!)

Copy link
Copy Markdown
Collaborator

@wise-king-sullyman wise-king-sullyman left a comment

Choose a reason for hiding this comment

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

I have one small suggestion but this LGTM overall so I'm comfortable approving whether you change it or not.

Comment on lines +207 to +227
...(isDisplayed
? [
{
id: node.id,
text: node.text,
isChecked,
checkProps: { 'aria-label': `Select ${node.text}` },
hasBadge: node.children && node.children.length > 0,
badgeProps: { isRead: true },
defaultExpanded: isChosen ? !!chosenFilter : !!availableFilter,
children: node.children
? buildOptions(isChosen, node.children, isFilterMatch || hasParentMatch)
: undefined
}
]
: []),
...(!isDisplayed && node.children && node.children.length
? buildOptions(isChosen, node.children, hasParentMatch)
: []),
...(remainingNodes ? buildOptions(isChosen, remainingNodes, hasParentMatch) : [])
];
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This is pretty dense, I think some newlines between the spreads would improve readability if nothing else.

@tlabaj tlabaj merged commit 5fe977b into patternfly:main Dec 8, 2021
@patternfly-build
Copy link
Copy Markdown
Collaborator

Your changes have been released in:

  • eslint-plugin-patternfly-react@4.19.0
  • @patternfly/react-catalog-view-extension@4.31.0
  • @patternfly/react-charts@6.33.0
  • @patternfly/react-code-editor@4.21.0
  • @patternfly/react-console@4.31.0
  • @patternfly/react-core@4.180.0
  • @patternfly/react-docs@5.41.0
  • @patternfly/react-icons@4.31.0
  • @patternfly/react-inline-edit-extension@4.25.0
  • demo-app-ts@4.140.0
  • @patternfly/react-integration@4.142.0
  • @patternfly/react-log-viewer@4.25.0
  • @patternfly/react-styles@4.30.0
  • @patternfly/react-table@4.49.0
  • @patternfly/react-tokens@4.32.0
  • @patternfly/react-topology@4.27.0
  • @patternfly/react-virtualized-extension@4.27.0
  • transformer-cjs-imports@4.18.0

Thanks for your contribution! 🎉

@nicolethoen nicolethoen deleted the dual_list_clean_up branch February 8, 2023 13:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DualListSelector - tree variant performance improvements DualListSelector: create composable tree example

5 participants