react-use icon indicating copy to clipboard operation
react-use copied to clipboard

[useMeasure] Ability to track a custom Ref

Open ashubham opened this issue 4 years ago • 15 comments

Is your feature request related to a problem? Please describe.

Currently useMeasure returns a ref which needs to be assigned a ref in the JSX. This has 2 issues:

  1. The ref is not a real useRef for eg. Does not have ref.current as a prop.
  2. If I already have a component where I have a ref, I just want to track its size changes.

Describe the solution you'd like

An API of this shape: const {width, height ... } = useMeasure({ref: currentRef});

Describe alternatives you've considered

I am using https://github.com/ZeeCoder/use-resize-observer ... in the absence of the above which I want to remove.

ashubham avatar May 18 '20 20:05 ashubham

+1 on this. I already have my ref so it will be useful to pass a ref as an argument. This will also give the ability to use more than one useMeasure in the same component, as I need to track three different elements.

michelecocuccio avatar May 19 '20 18:05 michelecocuccio

I just ran in the same issue, would also love this!

evadecker avatar Jun 06 '20 21:06 evadecker

Hey! How do you think, should we pass a real ref there or just an element?

const {width, height ... } = useMeasure({ ref: ref });

or

const {width, height ... } = useMeasure({ element: ref.current });

I am asking because ref is not used inside so we don't need it.

dmitrij-borchuk avatar Jun 09 '20 18:06 dmitrij-borchuk

Looks like this proposal contains breaking backward compatibility. Should new PR implement it like that? Currently, it returns an array where the first element is ref, but with passing ref as an argument, we don't need this returning ref. In case we need to save backward compatibility the interface will be strange:

const rect = useMeasure({ ref: elementRef })[1];

or

const { '1': stateArgs } = useMeasure({ ref: elementRef });

dmitrij-borchuk avatar Jun 09 '20 19:06 dmitrij-borchuk

@ashubham @streamich What do you think?

dmitrij-borchuk avatar Jun 17 '20 08:06 dmitrij-borchuk

what if you create and return a ref if no ref was passed in?

joeshub avatar Jun 17 '20 22:06 joeshub

@joeshub Great idea for the JavaScript but how TypeScript types should look like in this case?

dmitrij-borchuk avatar Jun 18 '20 11:06 dmitrij-borchuk

+1 for supporting a passed ref

priley86 avatar Sep 07 '20 15:09 priley86

@joeshub Great idea for the JavaScript but how TypeScript types should look like in this case?

Could be something like that ?

<E extends HTMLElement = HTMLElement>(element?: E): UseMeasureResult<E>

If the element parameter is passed, you use it to initiate the element state in the hook. This way, there is no breaking change.

You could use the hook like that :

const [, { width, height,... }] = useMeasure(element);

Looks like this proposal contains breaking backward compatibility. Should new PR implement it like that? Currently, it returns an array where the first element is ref, but with passing ref as an argument, we don't need this returning ref. In case we need to save backward compatibility the interface will be strange.

@dmitrij-borchuk Another solution could be to have two distinct types for the hook :

{
  <E extends HTMLElement = HTMLElement>(): UseMeasureResult<E>;
  <E extends HTMLElement = HTMLElement>(element: E): UseMeasureRect;
}

This would keep backward compatibility, without having a too weird interface. But this will add more complexity in the code, so I'm not sure it's worth it. What do you think ?

PoOw avatar Sep 15 '20 09:09 PoOw

@PoOw Thank you for the idea, you are awesome! I totally forget that TypeScript has Overloads (https://www.typescriptlang.org/docs/handbook/functions.html#overloads). I will work around that solution

dmitrij-borchuk avatar Sep 17 '20 16:09 dmitrij-borchuk

https://twitter.com/erikras/status/1361606811971952641

saiichihashimoto avatar Feb 25 '21 19:02 saiichihashimoto

+1 Or, an alternative way would be exposing useMeasureDirty. Currently I can only import it like this: import useMeasureDirty from 'react-use/lib/useMeasureDirty'; But it works well. It takes a real ref as and argument and then returns the dimension. Not sure why it's not in the export list

yf-hk avatar Mar 15 '21 07:03 yf-hk

Hi all! @react-hookz/web, the new library by one of react-use's former maintainers (background here and here) has a new implementation of useMeasure that solves point (1) of this issue, which might make point (2) moot for most users.

For those interested, there's an official migration guide for migrating from react-use to @react-hookz/web.

Hope this helps!

JoeDuncko avatar Sep 02 '21 00:09 JoeDuncko

is there any update on this? Passing ref from useMeasure as a ref gives typescript error as it is not a accepted ref object: (property) ref?: ((instance: HTMLDivElement | null) => void) | React.RefObject<HTMLDivElement> | null | undefined

terragady avatar May 25 '22 13:05 terragady

is there any update on this? Passing ref from useMeasure as a ref gives typescript error as it is not a accepted ref object: (property) ref?: ((instance: HTMLDivElement | null) => void) | React.RefObject<HTMLDivElement> | null | undefined

Hi @terragady , react-use is effectively no longer maintained.

@react-hookz/web, the new library by one of react-use's former maintainers (background here and here) has a new implementation of useMeasure that solves point (1) of this issue, which might make point (2) moot for most users.

Hope that helps!

JoeDuncko avatar May 25 '22 13:05 JoeDuncko