brimdata / react-arborist

The complete tree view component for React
MIT License
2.97k stars 134 forks source link

How do you implement controlled select? #243

Open hyperknot opened 5 months ago

hyperknot commented 5 months ago

I'm using this in a controlled way. My problem is that it seems it's not possible to do a controlled select.

I mean the whole component is controlled, but select is going through a spaghetti (in my case) of onSelect + treeAPI.deselectAll() + treeApi.selectMulti().

But then it can get in an infinite loop, with a selectMulti triggering onSelect, etc. triggering selectMulti, etc.

Normally in React these are solved by controlled components. In Arborist's case, it'd be something like a selectedMap={} property, which takes a Map of selected id and selects them.

How do you solve this problem?

Pancham97 commented 3 months ago

Okay, so I got around this with a little hack if you will. I overrode the props of the Node and passed a locally maintained state to the checkbox.

<Tree
  ref={treeRef}
  data={data}
  disableDrag
  disableMultiSelection={false}
  className="selection-tree"
  onSelect={handleSelect}
>
  {(props) => <Node isSelected={isSelected} setIsSelected={setIsSelected} {...props} />}
</Tree>
hyperknot commented 3 months ago

Yes, that's exactly what I did as well. Basically I manage the whole selected state myself, instead of using the built-in one.

On Wed, 29 May 2024 at 14:59, Pancham Khaitan @.***> wrote:

Okay, so I got around this with a little hack if you will. I overrode the props of the Node and passed a locally maintained state to the checkbox.

<Tree ref={treeRef} data={data} disableDrag disableMultiSelection={false} className="selection-tree" onSelect={handleSelect}> {(props) => <Node isSelected={isSelected} setIsSelected={setIsSelected} {...props} />}

— Reply to this email directly, view it on GitHub https://github.com/brimdata/react-arborist/issues/243#issuecomment-2137356301, or unsubscribe https://github.com/notifications/unsubscribe-auth/AADYVD3MPTSHVSRRJ3G2M4TZEXGKHAVCNFSM6AAAAABGE4G2I6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDCMZXGM2TMMZQGE . You are receiving this because you authored the thread.Message ID: @.***>

Joshlha commented 1 month ago

@Pancham97 Can you give a little more detail on this? Been struggling with implementing a solution for a bit now.

I'm not sure why you're passing in setIsSelected as props. Where is that coming from? Is it a function that exists in your outer react component (outside of the tree?)

Thanks

Pancham97 commented 1 month ago

@Pancham97 Can you give a little more detail on this? Been struggling with implementing a solution for a bit now.

I'm not sure why you're passing in setIsSelected as props. Where is that coming from? Is it a function that exists in your outer react component (outside of the tree?)

Thanks

@Joshlha yeah pretty much. Since React Arborist wouldn't maintain the state of selection, I made my own state using useState and pass the state and the setter as props. Whenever the checkbox is selected, it calls the setState method. I could whip up a small code gist if you want.

Joshlha commented 1 month ago

@Pancham97 I think I get what you're saying. I would imagine that isSelected holds the state of all nodes? And the set function sets the state of a particular node.

A code sample would be extremely helpful! I've been fighting with the built-in selection management provided by the tree and it's just not very flexible.

Pancham97 commented 1 month ago

@Joshlha I agree.

Here's something that I might have done. I do not have the exact piece of code because I didn't think this was an elegant use of a tree. For my use-case, I went with the standard accordions since that fit my need better.

function Node({ isSelected, setIsSelected, ...props }) {
  return (
    <input
      type="checkbox"
      checked={isSelected}
      onChange={() => setIsSelected(!isSelected)}
      {...props}
    />
  );
}

function Parent() {
  const [isSelected, setIsSelected] = React.useState(false);
  return (
    <Tree
      ref={treeRef}
      data={data}
      disableDrag
      disableMultiSelection={false}
      className="selection-tree"
      onSelect={handleSelect}
    >
      {(props) => (
        <Node
          isSelected={isSelected}
          setIsSelected={setIsSelected}
          {...props}
        />
      )}
    </Tree>
  );
}