bvaughn / react-resizable-panels

Home Page:https://react-resizable-panels.vercel.app/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Add way to rerender when isCollapsed changes

viveleroi opened this issue · comments

One problem with the ref API approach is that when something changes value, a render is not triggered. That's the point of refs obviously. Until we can support container queries in CSS we need to rely on the isCollapsed api method to set a CSS class in a collapsible panel.

In order to implement this we need to add a custom isCollapsed useState value/setter, set it using onCollapse and onExpand, and read it in our css class logic.

I dislike having to duplicate "state" like that - the value is already contained and controlled inside your logic, and I dislike having to duplicate those 3 extra things for each panel we need this logic for.

Maybe a solution could be adding a useResizer hook that returns the ref, and the isCollapsed and related booleans as state, allowing anyone who needs to trigger a render to do so.

Add way to rerender when isCollapsed changes

The recommended way to do that is like so:

// Your component
const [isCollapsed, setIsCollapsed] = useState(false);

<Panel onCollapse={() => setIsCollapsed(true)} onExpand={() => setIsCollapsed(false)} />

I've written some non-trivial apps with the current API and found it to be pretty easy/efficient to work with (not overly cumbersome).

I dislike having to duplicate "state" like that - the value is already contained and controlled inside your logic, and I dislike having to duplicate those 3 extra things for each panel we need this logic for.

Maybe a solution could be adding a useResizer hook that returns the ref, and the isCollapsed and related booleans as state, allowing anyone who needs to trigger a render to do so.

I understand why you'd prefer not to have the redundant state, but that's a subjective preference and the approach you describe (useResize) is not one that I'm inclined to add to this library.

If you dislike the "extra state" you could always write a custom hook, or a wrapper/higher-order component, that exposes it in a different way. I think the primary thing that's missing from doing this in a "nice" way is an API to subscribe to changes in collapsed state. I would probably be willing to add that, which would enable you to do this:

const panelRef = useRef(null);

// You could wrap this into a custom hook so you didn't need to repeat it
const isCollapsed = useSyncExternalStore(
  (change) => {
    const panel = panelRef.current;
    if (panel) {
      return panel.listenForResize(change);
    } else {
      return () => {};
    }
  },
  () => panelRef.current?.isCollapsed(),
  () => panelRef.current?.isCollapsed(),
);

<Panel ref={panelRef} />

That's fine, that's what we do and I figured this would be your reply. Thanks

Would you find an API like listenForResize to be particularly useful?

Would that be a way to cover onCollapse/onExpand with a single listener? I'd use that, although depending on how it fires during actual resizing it may get spammy, although we could debounce/etc.

Is there a discord or a more appropriate place to ask questions? Normally I'd use the discussions for some of these things. We've realized that because we use pixel values we need to specifically re-set the panel's size when it's collapsed and the user resizes the window.

There is no Discord. Most of my OSS projects are maintained by me alone, so I wouldn't really be able to keep up with a chat. I did just enable the discussion feature though.

Would that be a way to cover onCollapse/onExpand with a single listener?

Well, yes, essentially. It would provide a way to listen without mirroring state (if you wanted to write a custom hook for this). Otherwise you'd have to wrap with an HOC (or just copy/paste the mirrored state pattern I showed above).