facebook / relay

Relay is a JavaScript framework for building data-driven React applications.
https://relay.dev
MIT License
18.36k stars 1.82k forks source link

Suggestion: add Optional varieties to useQuery #2936

Open Togrias opened 4 years ago

Togrias commented 4 years ago

I'm not sure if this is 'supported/intended' behaviour, but QueryRenderer allows you to pass an undefined query prop. If no query prop is supplied, then no data is fetched.

This is useful for some components which only need to fetch data under certain circumstances.

The new experimental hooks have no way of omitting the first argument.

I suggest introducing optional varieties of the use LazyLoad / Preloaded Queries that allow the conditional fetching of data.

dminkovsky commented 4 years ago

I've struggled with this in every version of Relay, but I think experimental Relay would have you "render-as-you-fetch" with preloadQuery/usePreloadedQuery, as demonstrated in this sandbox, where preloadQuery is setResource(fetchTranslation(value)) and usePreloadedQuery is resource.read(). Since fetching happens during state initialization and then callbacks, you can set the state to null (when you don't want a query (like when an input is empty). I appreciate this pattern a lot, but have found it introduces some issues of its own. Any insights would be much appreciated!

Togrias commented 4 years ago

@dminkovsky following your example, calling usePreloadedQuery where preloadedQuery is null would throw an error. My main issue is that hooks can't be skipped during render. So even if you know that preloadedQuery is null, you can't skip calling usePreloadedQuery(). Compare to the old <QueryRenderer/> component, where one can pass query={shouldSkip? undefined : graphql...} as a prop to "disable" it.

dminkovsky commented 4 years ago

@Togrias as I implemented it, usePreloadedQuery() lives in its own component, and that component is conditionally rendered based on whether preloadQuery is null. The suspense boundary is then around the component that contains usePreloadedQuery(). Something like:

{preloadQuery && <Suspense><Component preloadQuery={preloadQuery} /></Suspense>}

where

const Component = ({preloadQuery}) => {
  const result = usePreloadedQuery(/*preloadQuery and other args*/)
  // ... render
}

If a query is loading and is not ready, it'll suspend. If there's nothing to load, nothing will render.

Togrias commented 4 years ago

Yes, I'm using a workaround like your method too but I think it's like a step backwards since the current QueryRenderer allows optional queries. Also, conditionally rendering elements may force unwanted mounting/unmounting while switching "modes". Hence my suggestion.

sibelius commented 4 years ago

How do you skip queries using QueryRenderer?

Togrias commented 4 years ago

<QueryRenderer query={skip? undefined : graphql...} .../>

dminkovsky commented 4 years ago

@Togrias yeah there's things I don't like about it, too. I just like the "render-as-you-fetch" concept, starting the request in the callback, and passing a "resource" that represents that fetch up into state, to render when ready. And it offers a way to fetch optionally.

That said, I've never understood why optional fetching has never been an explicitly accounted-for use-case. Made me wonder how other people handle it, because it seems to come up all the time for me (eg. empty input).

stale[bot] commented 3 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

xyy94813 commented 3 years ago

Are there any new solution or workaround on this issue?

stale[bot] commented 2 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

LucasPickering commented 1 year ago

For anyone still struggling with this, I've created a package that makes it easier to optionally render relay components. It is just a higher-order component that will wrap your Relay consumers so you can pass in a possibly-null queryRef to the wrapper and it will only render the child if it's defined. There are some examples in the repo. It's still a little rough so any feedback is appreciated.

https://github.com/LucasPickering/relay-query-wrapper