-
-
Notifications
You must be signed in to change notification settings - Fork 120
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: expose hash from useRouter
#746
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. |
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.
Thanks for the suggestion!
export default function TestRouter() { | ||
const router = useRouter_UNSTABLE(); | ||
// TODO: Why is this necessary? On the client `searchParams` | ||
// is a URLSearchParams object, but on the server it's an array. |
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 didn't know it's an array on the server. How can it be??
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.
Not sure, but without that, it breaks with get is not a function
. And the debugger showed me an array. Maybe node version differences?
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.
Can you dig into it further? If it's a bug in Waku, we would like to fix it.
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.
Sorry, took me some time to get back to this. I was not able to pinpoint whats the problem. This is what I have found.
At this point, searchParams
is still an URLSearchParams
object:
waku/packages/waku/src/router/define-router.ts
Lines 217 to 228 in f61b264
const input = getInputString(pathname); | |
const body = createElement( | |
ServerRouter as FunctionComponent< | |
Omit<ComponentProps<typeof ServerRouter>, 'children'> | |
>, | |
{ route: { path: pathname, searchParams } }, | |
componentIds.reduceRight( | |
(acc: ReactNode, id) => createElement(Slot, { id, fallback: acc }, acc), | |
null, | |
), | |
); | |
return { input, body }; |
But when ServerRouter
gets rendered, it has become []
:
waku/packages/waku/src/router/client.ts
Lines 530 to 552 in f61b264
export function ServerRouter({ | |
children, | |
route, | |
}: { | |
children: ReactNode; | |
route: RouteProps; | |
}) { | |
return createElement( | |
Fragment, | |
null, | |
createElement( | |
RouterContext.Provider, | |
{ | |
value: { | |
route, | |
changeRoute: notAvailableInServer('changeRoute'), | |
prefetchRoute: notAvailableInServer('prefetchRoute'), | |
}, | |
}, | |
children, | |
), | |
); | |
} |
My guess is that the route definition gets serialized by react when passed into the ServerRouter
client component and the empty URLSearchParams
map gets turned into []
?
With ddb4086 I removed the additional construction of URLSearchParams
to see if it also happens on CI, which it does.
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.
@dai-shi : i tried to find out more, but basically thats it. An empty URLSearchParams
object gets passed as a prop into createElement
and inside the ServerRouter
its an empty array. It has to be reacts prop-handling. Maybe it would be safer to stringify searchParams
?
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.
Yeah, you are absolutely right.
repro:
$ npm create waku@next
$ cd ...
$ npm i
$ cat foo.cjs
const { renderToPipeableStream } = require('react-server-dom-webpack/server');
const data = new URLSearchParams();
renderToPipeableStream(data).pipe(process.stdout);
$ node --conditions react-server foo.cjs
0:[]
I will open another PR to make it a string and see how it goes.
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.
To be transparent, this is the code that we save in the consumer: const router = useRouter();
const [hash, setHash] = useState('');
useEffect(() => {
setHash(window.location.hash);
}, [router]); Its not gigantic, but |
Co-authored-by: Daishi Kato <dai-shi@users.noreply.github.com>
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.
Sorry for the delay.
It looks good overall. Comments below.
export default function TestRouter() { | ||
const router = useRouter_UNSTABLE(); | ||
// TODO: Why is this necessary? On the client `searchParams` | ||
// is a URLSearchParams object, but on the server it's an array. |
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.
Can you dig into it further? If it's a bug in Waku, we would like to fix it.
packages/waku/src/router/client.ts
Outdated
...parseRoute(new URL(window.location.href)), | ||
// Make sure there is no hash on the initial route, | ||
// so it matches the server-rendered version. Otherwise | ||
// there might be hydration errors. | ||
hash: '', |
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.
It looks a bit hacky. Maybe we make parseRoute
to accept an option?
Co-authored-by: Daishi Kato <dai-shi@users.noreply.github.com>
Necessary for the initial render, where the hash would cause a hydration mismatch.
#746 revealed that `URLSearchParams` doesn't work. (In the near future, I will also rework on fetchRSC to eliminate it.)
@pmelab Hi, can you merge |
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.
2nd review
packages/waku/src/router/client.ts
Outdated
@@ -301,8 +305,15 @@ const InnerRouter = ({ routerData }: { routerData: RouterData }) => { | |||
const refetch = useRefetch(); | |||
|
|||
const [route, setRoute] = useState(() => | |||
parseRoute(new URL(window.location.href)), | |||
parseRoute(new URL(window.location.href), true), |
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.
So, ignoreHash
is used only here.
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.
Yes, without this, there are hydration errors, because the server has no hash, but the first client render does.
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.
Oh, I see. Can you leave some comments in the code?
I might refactor things around here in the future.
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 added some explanations there
Co-authored-by: Daishi Kato <dai-shi@users.noreply.github.com>
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.
3rd review.
packages/waku/src/router/client.ts
Outdated
// Update the route post-load to include the current hash. | ||
useEffect(() => { | ||
setRoute(parseRoute(new URL(window.location.href))); | ||
// eslint-disable-next-line react-hooks/exhaustive-deps |
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.
Hm, I wonder why we need the exception.
packages/waku/src/router/client.ts
Outdated
|
||
// Update the route post-load to include the current hash. | ||
useEffect(() => { | ||
setRoute(parseRoute(new URL(window.location.href))); |
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.
This actually triggers re-renders every time, even if the hash is ''
. We should probably improve it.
working on it. |
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.
Should be good.
What was changed?
Expose the current url hash via
UNSTABLE_useRouter
. On the server, its always an empty string, but gets populated on the client after the initial render.Why was it changed?
Otherwise inconvenient
useEffect
-gymnastics are necessary to get the current hash reliably into a client component.Notes
In the process of writing tests I found another potential issue with
searchParams
.https://github.com/dai-shi/waku/pull/746/files#diff-a8dab8847d02ba73c6b66e738495556ddda5d2dd01b70322aae006f7669ea237R6-R11
I'm not sure if we should move the double-creation of
URLSearchParams
into theuseRouter
hook. Given some advice, I can try to fix that as well.