-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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
feat(remix-react): add useRouteData
hook
#1281
Conversation
I built this hook yesterday. +1 for getting it into the Remix Utils 👍 |
This is already in Remix Utils, this PR is to add it directly in Remix which is better because it can use the internal context. |
Another reason I'm in favor of this improvement. |
+1, this would be awesome to have as part of Remix itself. |
We only ever intended I'm not sure why we're so bothered by child routes accessing parent data with it 🙃
I've already made my own @mjackson I want to get your thumbs up on this, but I think we should add it. I'd change this to just use route ids to access the data. We already exposed route ids as public API in export function meta({ parentsData }) {
let { user } = parentsData["root"];
let whatever = parentsData["routes/whatever"];
// ...
} This hook is not any different: export function Comp() {
let { user } = useRouteData("root");
let whatever = useRouteData("routes/whatever");
// ...
} It's far more convenient than @sergiodxa if @mjackson gives the thumbs up, I'd like to see the hook just accept route ids and not require the handle or url paths. |
I've also put a lot of thought into this and agree we should have this feature with the route ID as mentioned. We just need to take care in the docs to explain the trade-offs so people don't use it unnecessarily. |
The hook of this PR uses the route ID, the hook of Remix Utils doesn’t because it was not possible. |
+1 niceee |
+1 for this feature. It would help when sending data to a resource route whose parent already has that data. Right now, one would have to use the loader hook to request the data twice: once for the parent route and once for the resource route. |
You can already use |
Would it? This is still a hook, isn't it? |
This will not solve that, because this is a React hook, it'll make it easier to get another route data on React but server-side you still need to get the same data on each loader it's needed. |
@sergiodxa Can you fix conflicts pretty please? |
Co-authored-by: Mehdi Achour <machour@gmail.com>
@machour done |
This hook returns the JSON parsed data from the route loader function of any matching route in the current URL. | ||
|
||
```tsx filename=app/routes/invoices.tsx | ||
export function loader() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we're in a TS file
export function loader() { | |
export const loader: LoaderFunction = () => { |
|
||
```tsx filename=app/routes/invoices.tsx | ||
export function loader() { | ||
return fakeDb.invoices.findAll(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
action
& loader
need to return Response
or use json
helper
return fakeDb.invoices.findAll(); | |
return json(await fakeDb.invoices.findAll()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not 100% sold on this requirement. I know we already merged some docs changes that said we have to do this, but I'd like to find a way to still allow people to return whatever data they want and we just encode/decode for them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a lot of the confusion about typing loader return value and useLoaderData have to do with Remix allowing you to return a naked object from the loader.
I strongly recommend deprecating this and having them return an actual Response. That way there is no magic that leads to misunderstanding. They will know that loaders always return a Response.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -0,0 +1,22 @@ | |||
import type { LoaderFunction } from "remix"; | |||
import { Outlet, useRouteData } from "remix"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
import { Outlet, useRouteData } from "remix"; | |
import { json, Outlet, useRouteData } from "remix"; |
import { Outlet, useRouteData } from "remix"; | ||
|
||
export let loader: LoaderFunction = async () => { | ||
return { title: "Parent Route Data" }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return { title: "Parent Route Data" }; | |
return json({ title: "Parent Route Data" }); |
@@ -0,0 +1,17 @@ | |||
import type { LoaderFunction } from "remix"; | |||
import { useRouteData } from "remix"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
import { useRouteData } from "remix"; | |
import { json, useRouteData } from "remix"; |
import { useRouteData } from "remix"; | ||
|
||
export let loader: LoaderFunction = async () => { | ||
return { title: "Child Route Data" }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return { title: "Child Route Data" }; | |
return json({ title: "Child Route Data" }); |
|
||
afterEach(() => browser.close()); | ||
|
||
describe("the parent should render the child title", () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As @kentcdodds also requested in #2411 (comment)
I think we’re trying to use Ryan’s
createFixture
utility as much as possible instead of the gist app. Could you switch this test to use that instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. We are trying to get rid of the gists app for testing stuff and use a separate createFixture
test instead. Thanks!
useRouteData
hook
Thanks @sergiodxa and @Mange for the reminder. You all are both correct. Hooks have to top level. |
Closing this not because we don't want the feature, but because we'll be introducing it into React Router instead (as well as many other Remix APIs). Stay tuned, and thanks @sergiodxa for the work here! |
In Remix Utils I created this useRouteData hook which uses the
useMatches
hook to get the data of any route in the current URLThe way it works is you pass the route path and you get the data back, but if the route is a layout the path may be repeated, in that case I had to ask the devs to define an
id
in thehandle
export to search using that.This PR moves this useRouteData hook and taking advantage of being able to access the internal context of Remix you can with this implementation pass the route ID (the file name without the extension), which is easy to know for the developer.
Why is this hook useful?
A super common requirement is to access the data of the parent route inside the child route, most people put that in the
<Outlet context />
but it makes no sense when Remix already have the data of the loader inside a context, using useMatches is a way to solve it in user-land but this hook has a simpler implementation and could be used out of the box.And, contrarian to the Outlet solution, you can also access a child route data from a parent one, so you can communicate data in both directions from top to bottom or from bottom to top.