I have a callback route in my application that receives access token from auth process in URL hash fragment. I'd like to parse it and save into redux store. I used to get it through window.location.hash
before I switched to next, but this is not available when page renders server-side (also the hash fragment apparently isn't sent to server, so I can't parse it there).
Is there any way to restrict some code to execute only in client-side? Or is there any other way to achieve this?
@vanniewelt : Dynamically imported Async component loads on client and you can put all client side logic there.
Also ComponentWillReceiveProps lifecycle will receive request in props while at server side.
Can put check if this variable exists and inject your client code.
If the lib needs window variable, i suggest you use Dynamic imports, works well for me.
https://github.com/zeit/next.js/tree/v3-beta/examples/with-dynamic-import
You can also use the componentDidMount
lifecycle method of React. It's not called server side.
@vanniewelt You can always look at process.browser
..
if (process.browser) {
// client-side-only code
}
edit by @Timer:
@vanniewelt You can always look at typeof window
..
if (typeof window !== 'undefined') {
// client-side-only code
}
yes but it's not the best approach.
let's say I want to check if user is authenticated, and I want to run that check only on client side for several reason, one of them is because I'm storing JWT in localStorage and don't want to store it in a cookie.
So in this case on first page load with :
if (process.browser) {
// client-side-only code
}
code within the scope won't be executed at all, not on server not on client, it will be executed only if rout change was triggered from client
@sarkistlt You may be right in some cases depending on where that code is placed.. but..
If that code is executed on the server, execution will not reach // client-side-only code
. If that code is executed on the client, execution will reach // client-side-only code
.
It seems like a valid answer to the question:
Is there any way to restrict some code to execute only in client-side?
@sarkistlt componentDidMount
is only executed client-side, componentWillMount
is executed both client and server side.
@sarkistlt https://reactjs.org/docs/react-component.html#componentwillmount
This is the only lifecycle hook called on server rendering.
I did it a lot of times, you are confusing componentWillMount
with componentDidMount
.
@sergiodxa actually you right, have just run the test. thanks!
@sergiodxa componentWillMount
's link is changed to https://reactjs.org/docs/react-component.html#unsafe_componentwillmount.
I required mapbox-gl
module which is a client-side library inside ComponentDidMount
and got rid of an ugly error ReferenceError: self is not defined
I wasn't able to make Mapbox using componentDidMount
because the component still tries to be processed (I don't know what exactly is trying to do) on the server (I ended up having the same issue that @elaich).
Using Dynamic Imports as suggested by @aga5tya did the trick 馃帀.
@vanniewelt : Dynamically imported Async component loads on client and you can put all client side logic there.
Also ComponentWillReceiveProps lifecycle will receive request in props while at server side.
Can put check if this variable exists and inject your client code.If the lib needs window variable, i suggest you use Dynamic imports, works well for me.
https://github.com/zeit/next.js/tree/v3-beta/examples/with-dynamic-import
For those that have run into this thread searching for the solution. The Dynamic Imports link listed above is broken. It has been updated and can be found at Dynamic Imports.
If you have a component that's always supposed to be executed on the client, and don't want to use Dynamic Imports (e.g: people can forget that they need to use them), you can use Hooks (or the equivalent componentDidMount
) as follows:
import React, { useEffect, useState } from 'react'
function CreateInteraction ({ input }) {
const [isComponentMounted, setIsComponentMounted] = useState(false)
useEffect(() => setIsComponentMounted(true), [])
if(!isComponentMounted) {
return null
}
return <h1>I'm only executed on the client!</h1>
}
Just to add to some already good answers: My current project does a lot of conditional rendering in both directions, so I use a component for this:
import React, { useEffect, useState } from "react";
export interface ConditionallyRenderProps {
client?: boolean;
server?: boolean;
}
const ConditionallyRender: React.FC<ConditionallyRenderProps> = (props) => {
const [isMounted, setIsMounted] = useState(false);
useEffect(() => setIsMounted(true), []);
if(!isMounted && props.client) {
return null;
}
if(isMounted && props.server) {
return null;
}
return props.children as React.ReactElement;
};
export default ConditionallyRender;
And then use it as follows:
<Layout>
<ConditionallyRender server>
<p>This is rendered only on server.</p>
</ConditionallyRender>
<ConditionallyRender client>
<p>This is rendered only on client.</p>
</ConditionallyRender>
</Layout>
If you are using any library that access browser API then you can dynamically import modules with SSR=false as second argument.
const DynamicComponentWithNoSSR = dynamic(
() => import('../components/hello3'),
{ ssr: false }
)
https://nextjs.org/docs/advanced-features/dynamic-import
If you are using any library that access browser API then you can dynamically import modules with SSR=false as second argument.
const DynamicComponentWithNoSSR = dynamic( () => import('../components/hello3'), { ssr: false } )
https://nextjs.org/docs/advanced-features/dynamic-import
You will also need to add this import if you want to use "dynamic" import for anyone looking at @yashwant-dangi code!
import dynamic from 'next/dynamic'
@Timer I noticed you edited my comment https://github.com/vercel/next.js/issues/2473#issuecomment-362119102, which I again edited to reflect the original comment and your edit.
typeof window !== 'undefined'
instead of just process.browser
? It's more verbose, so if there's no reason here, I prefer to use process.browser
.Why should we use typeof window !== 'undefined' instead of just process.browser? It's more verbose, so if there's no reason here, I prefer to use process.browser.
process.browser
is non-standard and only available in webpack environment, meaning it's likely to break in the future, e.g. webpack 5 no longer polyfills process
.
Most helpful comment
@vanniewelt You can always look at
process.browser
..edit by @Timer:
@vanniewelt You can always look at
typeof window
..