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

useImageDimensions #87

Merged
merged 22 commits into from
Apr 16, 2020
Merged

useImageDimensions #87

merged 22 commits into from
Apr 16, 2020

Conversation

Greg-Bush
Copy link
Contributor

@Greg-Bush Greg-Bush commented Mar 18, 2020

Summary

This hook will solve the problem when you need to know the original image dimensions or aspect ratio for some complicated markup based on that image. And you might don't know what kind that image will be like: an asset or remote.
Depending on the "source" parameter type it uses one of the following methods:
https://reactnative.dev/docs/image/#resolveassetsource
https://reactnative.dev/docs/image/#getsize

Test Plan

Usage example in readme

Compatibility

OS Implemented
iOS
Android
Web

Checklist

src/useImageDimensions.ts Outdated Show resolved Hide resolved
@LinusU
Copy link
Member

LinusU commented Mar 18, 2020

Have you considered returning an object with { dimensions, loading, error } instead of having there error as a callback, and the dimensions be undefined when loading. Sort of like how useQuery from Apollo works, or useFetch? If yes, why made you not go that route?

@LinusU
Copy link
Member

LinusU commented Mar 18, 2020

Web | ❌

Doesn't Image.getSize work in react-native-web? 🤔

@Greg-Bush
Copy link
Contributor Author

Have you considered returning an object with { dimensions, loading, error } instead of having there error as a callback, and the dimensions be undefined when loading. Sort of like how useQuery from Apollo works, or useFetch? If yes, why made you not go that route?

Nice idea. Will do.

@Greg-Bush
Copy link
Contributor Author

Web | ❌

Doesn't Image.getSize work in react-native-web? 🤔

React Native for web, what? oO Ok then, it should work I guess.

@pvinis
Copy link
Member

pvinis commented Mar 19, 2020

This is a nice idea! Would you like to make a quick snack too and add it on the PR description?

@pvinis
Copy link
Member

pvinis commented Mar 19, 2020

Why did this not produce a canary release? 🤔

Edit: It's because this PR comes from a fork. I enabled the builds to run on PRs from forks, which is great for checking if tests and builds are green, but the canary release doesn't happen because of credentials. I guess it's alright for now, but I created #90 to see if we can enable that.

@pvinis pvinis added minor Increment the minor version when merged release Create a release when this pr is merged labels Mar 19, 2020
src/useImageDimensions.ts Outdated Show resolved Hide resolved
src/useImageDimensions.ts Outdated Show resolved Hide resolved
src/useImageDimensions.ts Outdated Show resolved Hide resolved
src/useImageDimensions.ts Outdated Show resolved Hide resolved
src/useImageDimensions.ts Outdated Show resolved Hide resolved
src/useImageDimensions.ts Outdated Show resolved Hide resolved
* @param source either a remote URL or a local file resource.
* @returns original image width and height.
*/
function useImageDimensions(source: ImageRequireSource | URISource) {
Copy link
Member

Choose a reason for hiding this comment

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

How about importing ImageSourcePropType from react-native and using that as the type for source?

Suggested change
function useImageDimensions(source: ImageRequireSource | URISource) {
function useImageDimensions(source: ImageSourcePropType) {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is a lot of redundant properties in ImageSourcePropType too, that are not using in the hook.

Copy link
Member

Choose a reason for hiding this comment

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

Is there any downside to using ImageSourcePropType, even though it has some more props defined though? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Here is about ImageSourcePropType:
https://reactnative.dev/docs/image#source
This prop can also contain several remote URLs, specified together with their width and height and potentially with scale/other URI arguments. The native side will then choose the best uri to display based on the measured size of the image container.
It means that ImageSourcePropType contains redundant types. No sense to use these types in this hook.

src/useImageDimensions.ts Outdated Show resolved Hide resolved
@Greg-Bush
Copy link
Contributor Author

This is a nice idea! Would you like to make a quick snack too and add it on the PR description?

Hey! What is a snack? What do you mean?

@LinusU
Copy link
Member

LinusU commented Mar 20, 2020

A snack is a way to easily share React Native code, mainly used for testing/demonstrating.

Everyone can create one at this site: https://snack.expo.io

It would be great if you could create an example that uses your hook there so that we could see it in action! 💪

@pvinis
Copy link
Member

pvinis commented Mar 21, 2020

But actually I don't know how this could work, since there is no way for @Greg-Bush to make a canary, right?

@Greg-Bush
Copy link
Contributor Author

Hey guys! I have made a snack. The Expo snack is working badly with remote images. I don't know the reason. But I have already tested that hook for mobile in the real project locally and it works ok.
I'm not sure that it will work on the web. Because I don't know how to test that.
Also, I took into account all the comments, and I guess this is the final version, so please consider to merge.

src/useImageDimensions.ts Outdated Show resolved Hide resolved
*/
readonly aspectRatio: number
width: number
height: number
Copy link
Member

Choose a reason for hiding this comment

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

How about making width and height readonly as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unlike 'aspectRatio', actually, these fields are not read-only.

Copy link
Member

Choose a reason for hiding this comment

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

I think it could be good to mark them as readonly so catch a potential error where the end user accidentally modifies them, since that will be reset each render loop. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't understand what do you mean and why do you think that field change will cause anything?

src/useImageDimensions.ts Outdated Show resolved Hide resolved
src/useImageDimensions.ts Outdated Show resolved Hide resolved
src/useImageDimensions.ts Outdated Show resolved Hide resolved
@LinusU
Copy link
Member

LinusU commented Mar 30, 2020

@Greg-Bush would you mind not pressing "Resolve" after answering comments? It becomes very hard for me to find your replies. Thanks! ☺️

@Greg-Bush
Copy link
Contributor Author

@Greg-Bush would you mind not pressing "Resolve" after answering comments? It becomes very hard for me to find your replies. Thanks! ☺️

Ok, clear

@Greg-Bush
Copy link
Contributor Author

Hey guys! Is there any decision about that pull request?

Copy link
Member

@LinusU LinusU left a comment

Choose a reason for hiding this comment

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

There are still a few things that I would like to be addressed ☺️

src/index.ts Outdated Show resolved Hide resolved
src/useImageDimensions.ts Outdated Show resolved Hide resolved
* @param source either a remote URL or a local file resource.
* @returns original image width and height.
*/
function useImageDimensions(source: ImageRequireSource | URISource) {
Copy link
Member

Choose a reason for hiding this comment

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

Is there any downside to using ImageSourcePropType, even though it has some more props defined though? 🤔

src/useImageDimensions.ts Show resolved Hide resolved
@Greg-Bush
Copy link
Contributor Author

@pvinis, the request has long been open. Please, decide here to merge this PR, or not.

@LinusU
Copy link
Member

LinusU commented Apr 16, 2020

Pushed a commit that typed the return value of the type, reused the return value as to not cause unnecessary re-renders, and cleaned up a bit. Landing now! Thanks for you contribution! 🌟

@LinusU LinusU merged commit 7bb9837 into react-native-community:master Apr 16, 2020
@pvinis
Copy link
Member

pvinis commented Apr 16, 2020

🚀 PR was released in v2.5.0 🚀

@pvinis pvinis added the released This issue/pull request has been released. label Apr 16, 2020
@Greg-Bush
Copy link
Contributor Author

Wow! Wtf? Why do you approve your changes and merge them without code review?

Copy link
Contributor Author

@Greg-Bush Greg-Bush left a comment

Choose a reason for hiding this comment

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

Useless change. No sense here at all.

@LinusU
Copy link
Member

LinusU commented Apr 16, 2020

Why do you approve your changes and merge them without code review?

I approved your change, and then I made a few changes as I landed. If you want to I'm sure that @pvinis can review them for me... I didn't think that this was controversial since I've seen it done a lot, and also other maintainers merges minor changes in this repo without someone else approving...

Useless change. No sense here at all.

Would you mind elaborating on what part was useless or not making sense? I'd be happy to update...

If you are talking about re-using the return value, I do not think that it's useless since it will make sure that e.g. the following code doesn't re-calculate every time:

const dimensions = useImageDimensions(...)
const foo = useMemo(() => /* compute with dimensions */, [dimensions])

// ...

@Greg-Bush
Copy link
Contributor Author

Ok, clear with "result".

get aspectRatio() {
return this.width / this.height
}
export type Source = ImageRequireSource | URISource
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why do you make one more type and export it?

Copy link
Member

Choose a reason for hiding this comment

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

If someone wants to build a hook that extends on this hook, it's nice to be able to have one type that represents any input. That way it can easily be used as the input value to a function and just passed on, e.g:

function useHeaderHeight (logo: Source, background: Source): number {
  // ...
}

}
export type Source = ImageRequireSource | URISource

export interface ImageDimensions {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why do you also export this one?

Copy link
Member

Choose a reason for hiding this comment

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

If someone wants to build a hook that extends on this hook, it's nice to be able to have one type that represents the return value. That way it can easily be used as the return value to a function and just passed on, e.g:

function useLogoDimensions (): ImageDimensions {
  return useImageDimensions(globalSettings.logoImage)
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

TypeScript calculates return types automatically, there is no need to mark it manually.
Also, the 'ImageDimensions' name doesn't fully represent the content of this interface. There are 'error' and 'loading' fields. And the sizes themselves are even in a nested property 'dimensions', this field may be called 'ImageDimensions'. The whole object, which contains all of these - not really.

Copy link
Member

Choose a reason for hiding this comment

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

I'd be open to renaming it ImageDimensionsResult or something similar...

TypeScript calculates return types automatically, there is no need to mark it manually.

Many style guides mandate that the return type be specified to avoid accidentally not retuning what you intended by clearly stating the intent up front. I think that's a good practice 👍

} catch (error) {
setResult({error, loading: false})
}
}, [source])
Copy link

@tuantranailo tuantranailo Jul 9, 2020

Choose a reason for hiding this comment

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

A question here: if the source is an object, wouldn't it be changed every render, triggering the warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or **one of the dependencies changes on every render.**? This is because useEffect hook does shallow comparison, not deep comparison. @Greg-Bush and @LinusU

Copy link
Member

Choose a reason for hiding this comment

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

It depends on how you pass in the source, as long as the same object is being passed each render cycle it will not trigger the useEffect to re-run. The one case I can see would be problematic is if people pass the url as such:

useImageDimensions({ uri: 'https://...' })

We could help with this by doing something like:

-  }, [source])
+  }, [source?.uri ?? source])

Choose a reason for hiding this comment

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

@LinusU Yes. That would definitely do the trick. 👍 👍 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
minor Increment the minor version when merged release Create a release when this pr is merged released This issue/pull request has been released.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants