-
Notifications
You must be signed in to change notification settings - Fork 186
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
[ARGG-876] Add support for withScrimmedPortal with SSR #3085
Conversation
Visit https://backpack.github.io/storybook-prs/3085 to see this build running in a browser. |
* If consumer passes runOnServer as true (meaning they want to render the component on server and client), we render the component as normal once the component has been mounted. | ||
* This is to ensure the snapshotted markup (initial render before the component has been mounted) is the same on both server and client. | ||
*/ | ||
if (!runOnServer || isPortalReady) { |
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.
Would we expect a consumer to be aware of where the component is being rendered (server or browser) and act accordingly to pass a prop in? If so, this is a code smell to me.
Modern web frameworks like Remix have no real concept of "running on server" or "running on browser". The expectation is all components are executed in both environments and it's common practice for components to be required to be self aware of the where they are being rendered, and thus act accordingly.
What's the reason we don't want this to be self aware?
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.
Modern web frameworks like Remix have no real concept of "running on server" or "running on browser". The expectation is all components are executed in both environments
I didn't know that, but I think that's still ok as then you just pass the runOnServer
prop which means it runs on both (not just server)? we can make that the default instead (this component is not used anywhere, so we have the freedom of making any change to it).
and it's common practice for components to be required to be self aware of the where they are being rendered, and thus act accordingly.
How can we tell if a component is running on server?
I guess my thoughts here were that consumers can choose whether they want to render on both and use the BpkScrim
on server, OR just render it on client.
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 that, but I think that's still ok as then you just pass the runOnServer prop which means it runs on both (not just server)?
Then the naming here should be improved, runOnServer
suggests only the server.
But it still raises the question, do we need flexibility here? Why can't this component decide what should happen depending on the environment?
How can we tell if a component is running on server?
common practice is to check for the presence of window, but it depends what the requirements are. If we need the document
to be available, we should check for that.
if (typeof window === 'undefined')
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.
totally agree with Chris and probably that's the source of my confusion in the other comment
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.
Then the naming here should be improved, runOnServer suggests only the server.
I thought we always run on either just client, or both client and server so it's clear that this will run on both (although also added a comment above to explain it)
But it still raises the question, do we need flexibility here? Why can't this component decide what should happen depending on the environment?
The component does decide what should happen depending on the env, I just didn't want to force the BpkScrim
on the initial render on the client unless required i.e. to match the server initial render also thinking whether maybe in the future we want to render something else on the server, not the BpkScrim
Maybe this comment helps here https://github.com/Skyscanner/backpack/pull/3085/files#r1395443998
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.
Do all our microsites run on both client and server completely?
There's a big mix. Banana is CSR only. Falcon and hotels do SSR. OCs SSR the HTML and deliver it when requested.
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.
Agree with previous comments here, replacing runOnServer
prop and it's associated behaviour with if (typeof window === 'undefined')
should do the trick 👍
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.
Ah, yes, that'll work too, we don't need the prop
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've removed the prop, although just to clarify - the point of the runOnServer
prop was to set the expectation whether the component will run only on client only or if it'll run on both. I think using the window
object helps us determine whether at run time it's running on server or on client, but I think achieving the same as the prop (i.e. if the intent is for the component to run on both client and server, or just on client so that for client-only projects we don't need to first render the scrim) makes it confusing as when it's running on client we'd need to check if it's already ran on server 🤔
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.
we'd need to check if it's already ran on server
Can you explain what you mean by this? I don't quite follow
Visit https://backpack.github.io/storybook-prs/3085 to see this build running in a browser. |
useEffect(() => { | ||
setIsPortalReady(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.
Given all the discussion regarding double render and the general strategy being taken here, I recommend linking to the react docs: https://react.dev/reference/react-dom/hydrate#handling-different-client-and-server-content and popping some of the context from the comments directly into code.
Visit https://backpack.github.io/storybook-prs/3085 to see this build running in a browser. |
Visit https://backpack.github.io/storybook-prs/3085 to see this build running in a browser. |
}); | ||
|
||
it('renders the wrapped component inside a portal with renderTarget provided', () => { | ||
const WrappedComponent = () => <div>Wrapped Component</div>; | ||
const ScrimmedComponent = withScrimmedPortal(WrappedComponent); | ||
const DialogContent = () => <div data-testid="dialog-content">Dialog content</div>; |
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.
what's the difference between this test and the next one?
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.
hmm, yeah, you're right. Nothing really, I'll remove it.
}); | ||
|
||
it('renders the wrapped component inside a portal with renderTarget provided', () => { | ||
const WrappedComponent = () => <div>Wrapped Component</div>; | ||
const ScrimmedComponent = withScrimmedPortal(WrappedComponent); | ||
const DialogContent = () => <div data-testid="dialog-content">Dialog content</div>; |
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.
what's the difference between this test and the next one?
@@ -17,7 +17,7 @@ | |||
*/ | |||
|
|||
import '@testing-library/jest-dom'; | |||
import { render, within, screen } from '@testing-library/react'; | |||
import { render, within, screen, isInaccessible } from '@testing-library/react'; |
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.
is isInaccessible
used somewhere?
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.
Ohh no, I'd added it, but then removed it
I'll remove this, I thought the linting would do that 😅
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.
Visit https://backpack.github.io/storybook-prs/3085 to see this build running in a browser. |
The
WithScrimmedPortal
BPK component is not compatible with SSR due to the use of the React Portal. To be able to use the component in projects that use SSR, we should provide a fallback behaviour that can be rendered on the server. The fallback behaviour should be theBpkScrim
component, however, in the future we can allow consumers to pass another component if needed.Remember to include the following changes:
README.md
(If you have created a new component)README.md