Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AutoSizer #5

Closed
bvaughn opened this Issue May 30, 2018 · 35 comments

Comments

Projects
None yet
9 participants
@bvaughn
Copy link
Owner

bvaughn commented May 30, 2018

Document an AutoSizer solution built with react-measure (based on the Resize Observer spec, can be polyfilled). This seems like a more future-friendly way of detecting resize.

For the moment, I have published a standalone version of the AutoSizer component from react-virtualized, as react-virtualized-auto-sizer.

@bvaughn bvaughn changed the title AutoSizer support AutoSizer May 30, 2018

@wuweiweiwu

This comment has been minimized.

Copy link

wuweiweiwu commented Jun 6, 2018

If you haven't coded this up yet. I'd pick it up. I've been meaning to try out react-measure.

@bvaughn

This comment has been minimized.

Copy link
Owner Author

bvaughn commented Jun 6, 2018

Nice! Reach out to Travis (@souporserious). He and I have been talking about this a bit.

My latest thinking is that react-window doesn't need an AutoSizer component. Instead, we can just document how to use react-measure for this purpose. (Maybe we could even release a light-weight wrapper component, but I don't think it should be in the main library- at least I'm not thinking that yet.)

@wuweiweiwu

This comment has been minimized.

Copy link

wuweiweiwu commented Jun 6, 2018

I see. Will do. I'll send him an email after I get a bit more familiar with react-measure.

I'm kinda curious on how we can measure the parent node or how to pass the measureRef to the parent node

@bvaughn

This comment has been minimized.

Copy link
Owner Author

bvaughn commented Jun 6, 2018

I was curious about the same thing! Here's what he said about it:

Ah I think I see what you mean now. Yeah let me update the repo this next week and I'll show you how the new API allows this. I give you a "formula" option now for every measureRef that you can customize how the measurements are handled, so in AutoSizers case we can use a custom formula that looks at it's parent's measurements and reports them when they change. Might need some tweaking, but I think it should work fine.

I love the idea of working with Travis on this because (1) this isn't a windowing problem, so it's nice to not have to own it in this library and (2) his approach seems more future looking than the one CellMeasurer used.

@wuweiweiwu

This comment has been minimized.

Copy link

wuweiweiwu commented Jun 6, 2018

That sounds really promising!

I'm going to test out a wrapper component that just calls the measureRef on the parent node. I wonder if that'll work.

I'll post a sandbox

@souporserious

This comment has been minimized.

Copy link
Contributor

souporserious commented Jun 6, 2018

Oh man, my apostrophe usage 🤦‍♂️ 💩

@wuweiweiwu glad to hear you're interested! I need to update the rewrite PR with some new code still. I have some time tomorrow night and should be able to get that done. I'd love feedback from either of you if you'd like to help, I'll reach out soon when it's updated so we can hopefully land on good solution 🤞.

@wuweiweiwu

This comment has been minimized.

Copy link

wuweiweiwu commented Jun 6, 2018

@souporserious Awesome! I look forward to it. I still have to get more familiar with react-measure under the hood :)

@bvaughn I make a janky proof of concept that works with some hacky code and a wrapper around Measure https://codesandbox.io/s/xll21o5lo4 . I would definitely prefer the formula option though

@souporserious

This comment has been minimized.

Copy link
Contributor

souporserious commented Jun 6, 2018

@wuweiweiwu nice work!!! That's similar to what I was thinking. Just a small note, there's a new API in the works that can be installed under an rc tag, it's slightly different from the old one. You can get to it in the dropdown in codesandbox when adding the package. I like the idea of just grabbing the parentNode and passing that to measureRef though! Maybe I can make some tweaks to react-measure to make that work easier 🤔.

I threw together a quick sandbox as well here: https://codesandbox.io/s/mzzzz5xmq9

That's wrapping two nodes though so it's not the nicest API. The one problem I'm foreseeing with the formula option is it won't be observing the parent's node for changes, you can only use that to grab the parent dimensions... so the other way this could work is by passing down the API. Wondering how you both feel about something like this 👇

<AutoSizer>
  {({ bindParent, bindChild, width, height }) => (
    <div {...bindParent}>
      <div {...bindChild}>
        {width} x {height}
      </div>
    </div>
  )}
</AutoSizer>

It's not my favorite though 😏 so maybe some react-measure tweaks with grabbing the parentNode and passing that to measureRef might be best.

@bvaughn

This comment has been minimized.

Copy link
Owner Author

bvaughn commented Jun 6, 2018

I'm not sure I see how this example ^ is that different from passing measureRef? In both cases, AutoSizer is measuring its child rather than its parent. So you have to worry about passing through the additional params.

The reasons that seems unfortunate to me are:

  • You need to handle more params in the child function.
  • Your AutoSizer is less reusable since it has to e.g. set a style like flex: 1 1 auto or something on the top level wrapper. (What if you want to use this outside of a flex container?)
  • It complicates the process of getting a ref to the inner e.g. list. (You'd have to use the new forwardRef API but that limits you to 16.3+.)
@souporserious

This comment has been minimized.

Copy link
Contributor

souporserious commented Jun 6, 2018

Yeah, that's similar to what I was thinking. I think I'll stick with the parentNode approach. Maybe I can add an easier imperative way to do it in react-measure for cases like AutoSizer. I'll mess around with some things tonight and get back to you.

@wuweiweiwu

This comment has been minimized.

Copy link

wuweiweiwu commented Jun 8, 2018

@souporserious awesome! please keep me up to date as well :)

@techniq

This comment has been minimized.

Copy link

techniq commented Jun 17, 2018

@vx/responsive has a ParentSize component that is also a lightweight wrapper around the Resize observer spec as well, although I've been eyeing the rewrite of react-measure and considering talking with @hshoff about adopting it. Also need to make sure it plays well with bounding box for tooltips/etc.

I've used both react-virtualized's AutoSizer and and @vx/respnosive's ParentSize with my mui-table component and have had good success. I currently use ParentSize in some non-open source code, although I've had an issue with Flexbox styling and resizing smaller (bigger typically works).

@souporserious

This comment has been minimized.

Copy link
Contributor

souporserious commented Jun 17, 2018

Hey @techniq love to hear that you're considering react-measure! Sorry, I've been slacking on the rewrite. I've been super busy with my main job, I keep pushing it off 😞. It's my next priority to wrap up though. We've been using it in our UI kit on my team at work for tooltips and popovers so hopefully it should translate well for your needs.

I've looked at the ParentSize component in the past for inspiration. Maybe if they are interested, once the new react-measure version is out, I could PR to use it for the measuring portion.

@techniq

This comment has been minimized.

Copy link

techniq commented Jun 17, 2018

@souporserious PR would be awesome when you have time.

There is a semi-related issue regarding porting more components to use render props (instead of HOCs). ParentSize was ported a while back (HOC is still available) but those listed in the issue are related (ScreenSize = window/viewport as parent, bounding box handling, all related to chart sizes and tooltips).

Completely understand putting your bill-paying job first, I'm in the same boat 😉 (wish I had more time for open source, and current job doesn't directly support working on it beyond using it to accomplish tasks).

@bvaughn

This comment has been minimized.

Copy link
Owner Author

bvaughn commented Jun 18, 2018

For the moment, I have published a standalone version of the AutoSizer component from react-virtualized, as react-virtualized-auto-sizer.

@MatteoGioioso

This comment has been minimized.

Copy link

MatteoGioioso commented Nov 19, 2018

I think I have an issue with Autosizer and VariableSizeList.
I have made my HOC component for detecting scroll and with Autosizer the scroll event handler does not get triggered. Any suggestion ?

const Example = () => (
<div className={parentAutosizer}>
  <AutoSizer>
      {({height, width}) => (
    <List
        height={150}
        itemCount={1000}
        itemSize={getItemSize}
        width={300}
     >
       {Row}
      </List>
      )}
    </AutoSizer>
  </div>
);

export const WindowScroll(Example)

and the styles are those

parentAutosizer: {
    position: 'absolute',
    top: 0
    bottom: 0
    left: '0',
    right: '0'
  }
@TrySound

This comment has been minimized.

Copy link
Contributor

TrySound commented Nov 19, 2018

@MatteoGioioso I believe the problem is hidden inside your WindowScroll HOC. Without codesandbox with the issue we cannot provide a good help.

@MatteoGioioso

This comment has been minimized.

Copy link

MatteoGioioso commented Nov 19, 2018

@TrySound

I think not. If I set manually height and width without the Autosizer the WindowScroll works fine.
Anyway, I will try to replicate it tomorrow with a codesandbox.
Thanks

@bvaughn

This comment has been minimized.

Copy link
Owner Author

bvaughn commented Nov 19, 2018

Hey @MatteoGioioso 👋

Sorry to hear you're having some trouble with AutoSizer. I would suggest you file a separate issue (since this issue is about documentation) but please don't create an issue unless you provide a full repro case (including your WindowScroll HOC) since it's not really possible for us to guess at what may be going wrong with code we can't see.

@MatteoGioioso

This comment has been minimized.

Copy link

MatteoGioioso commented Nov 20, 2018

@bvaughn Ok, I will do =)

@damiangreen

This comment has been minimized.

Copy link

damiangreen commented Dec 4, 2018

I'm a heavy user of antD and theyv'e been crying out for virtualization for a long time.

I'd like to help them but before i embark, I was just wondering what your current recommendations are for using react window with autosizer, is the react-virtualized-auto-sizer the one you are championing for use with react window for the medium to long term?

@bvaughn

This comment has been minimized.

Copy link
Owner Author

bvaughn commented Dec 4, 2018

Yes, until/unless there's a standard ResizeObservable based component– I have been using react-virtualized-auto-sizer myself.

(Note that souporserious/react-measure is something worth keeping an eye on, but I'm not sure if it's stalled or not.)

@souporserious

This comment has been minimized.

Copy link
Contributor

souporserious commented Dec 4, 2018

I'm planning on releasing a hooks version for react-measure that should work a little bit nicer for auto-sizing components.

@Pruxis

This comment has been minimized.

Copy link

Pruxis commented Jan 3, 2019

I'm currently utilising something that looks like this in the current alpha of React.
I like the ref approach as it gives the freedom of the developer what element size you want to keep track of instead of relying on component hierarchy.

import { useState, useEffect, Ref } from 'react';
import ResizeObserver from 'resize-observer-polyfill';

const useAutoresize = (elementRef: Ref) => {
  const [{ width, height }, setMeasurements] = useState({ width: 0, height: 0 });
  const observer = new ResizeObserver(([{ contentRect }]) => {
    setMeasurements({ width: contentRect.width, height: contentRect.height });
  });
  useEffect(
    () => {
      observer.observe(elementRef.current);
      return () => observer.disconnect();
    },
    [elementRef]
  );
  return { width, height };
};

export default useAutoresize;
@FezVrasta

This comment has been minimized.

Copy link

FezVrasta commented Feb 4, 2019

I'm probably going to attempt to implement AutoSizer for react-window, but if anyone else wants to work on it please do, I can't promise anything

@bvaughn

This comment has been minimized.

Copy link
Owner Author

bvaughn commented Feb 4, 2019

What do you plan on doing differently than react-virtualized-auto-sizer, @FezVrasta ?

@FezVrasta

This comment has been minimized.

Copy link

FezVrasta commented Feb 4, 2019

I released a very lightweight measurement library for React few years ago (400 bytes without any dependency) that works very well (x).

I want to use it to create the AutoSizer component, I think it would make sense to ship it with react-window (mostly because it wouldn't add more than 1kb to the final gzipped bundle size but is a must have feature for 99% of the virtual list users), but if you think it's better as a stand-alone library I'll go that way also.

Specifically, I'd love to provide a updateCellSize function to each cell component to make it trivial the size update. It's an approach we use internally in our virtual list component and proved to be very developer friendly.

Let me know if it makes sense.

@bvaughn

This comment has been minimized.

Copy link
Owner Author

bvaughn commented Feb 4, 2019

I probably don't want to add anything that isn't specifically about windowing to the base react-window library (including something like auto-sizer). I think it should be an add-on package.

I'd like to learn more about how you're using updateCellSize and how you see it being used with a library like this though!

@FezVrasta

This comment has been minimized.

Copy link

FezVrasta commented Feb 4, 2019

I'd like to learn more about how you're using updateCellSize and how you see it being used with a library like this though!

This is how it looks like the API we use internally.

import { FixedSizeList as List } from 'react-window';
 
const Row = ({ index, style, updateCellSize }) => {
  const [isOpen, setOpen] = useState(false);

  return (
    <div style={style} tabIndex={0} onClick={() => {
      setOpen(isOpen => !isOpen);
      updateCellSize();
    }}>
      Row {index}
      {isOpen && 'Lorem lipsum dolor sit amet, the quick brown fox jumps over the lazy dog, woff.}
    </div>;
  }
}
 
const Example = () => (
  <List
    height={150}
    itemCount={1000}
    itemSize={35}
    width={300}
  >
    {Row}
  </List>
);
@bvaughn

This comment has been minimized.

Copy link
Owner Author

bvaughn commented Feb 4, 2019

I don't think that's a complete enough example. What is updateCellSize? What does it do when it's called? Why do you need it in the first place if your rows are of a fixed size (35px)?

@FezVrasta

This comment has been minimized.

Copy link

FezVrasta commented Feb 4, 2019

@bvaughn the isOpen state may expand the div's size (yeah maybe the example wasn't that clear).

updateCellSize simply runs a cache.clear(index, 0) (where index is the cell index) and a forceUpdateGrid (even if I'm not sure if it's actually needed).

@bvaughn

This comment has been minimized.

Copy link
Owner Author

bvaughn commented Feb 4, 2019

the isOpen state may expand the div's size

FixedSizeList wouldn't support this. All rows are of a fixed size – in your example above, 35 pixels. Maybe you're using VariableSizeList?

@FezVrasta

This comment has been minimized.

Copy link

FezVrasta commented Feb 4, 2019

@bvaughn yeah so, right now we are using react-virtualized. I wanted to contribute here to be able to switch. But I guess I have some confusion in my head 😄

@bvaughn

This comment has been minimized.

Copy link
Owner Author

bvaughn commented Feb 4, 2019

I see. Yeah, VariableSizeList would work like react-virtualized does. It should work for you.

@bvaughn

This comment has been minimized.

Copy link
Owner Author

bvaughn commented Mar 8, 2019

For the time being, I have updated the README to document a solution using react-virtualized-auto-sizer. I'm going to close this issue for now since that solutions is good enough. Still happy to look into and talk about an approach based on ResizeObserver though!

@bvaughn bvaughn closed this Mar 8, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.