I'm trying to render render a ponent differently based on if the screen width < 768p.
If it is less than 768p, then load the hamburger menu. If not, load the entire menu.
Here's the code I use. The issue is, when the page first loads, I cannot observe the changes. But when I make the browser screen small and then increase it to it's original size; the effects take place.
I think it's because React is being rendered server-side. But still don't understand why it works by making the screen smaller and then bigger again.
import "twin.macro"
import { Global, css } from "@emotion/core"
import { useState, useEffect } from "react"
const Navbar = () => {
const useWindowDimensions = () => {
const hasWindow = typeof window !== "undefined"
function getWindowDimensions() {
const width = hasWindow ? window.innerWidth : null
const height = hasWindow ? window.innerHeight : null
return {
width,
height,
}
}
const [windowDimensions, setWindowDimensions] = useState(
getWindowDimensions()
)
useEffect(() => {
if (hasWindow) {
function handleResize() {
setWindowDimensions(getWindowDimensions())
}
window.addEventListener("resize", handleResize)
return () => window.removeEventListener("resize", handleResize)
}
}, [hasWindow])
return windowDimensions
}
const { height, width } = useWindowDimensions()
const breakpoint = 768
return (
<div>
{width <= breakpoint ? (
<div>
<HamburgerMenu />
</div>
) : (
<div>
<FullMenu />
</div>
)
}
export default Navbar
Is there a way I can force the Next.js app to apply the effects on the first render?
I'm trying to render render a ponent differently based on if the screen width < 768p.
If it is less than 768p, then load the hamburger menu. If not, load the entire menu.
Here's the code I use. The issue is, when the page first loads, I cannot observe the changes. But when I make the browser screen small and then increase it to it's original size; the effects take place.
I think it's because React is being rendered server-side. But still don't understand why it works by making the screen smaller and then bigger again.
import "twin.macro"
import { Global, css } from "@emotion/core"
import { useState, useEffect } from "react"
const Navbar = () => {
const useWindowDimensions = () => {
const hasWindow = typeof window !== "undefined"
function getWindowDimensions() {
const width = hasWindow ? window.innerWidth : null
const height = hasWindow ? window.innerHeight : null
return {
width,
height,
}
}
const [windowDimensions, setWindowDimensions] = useState(
getWindowDimensions()
)
useEffect(() => {
if (hasWindow) {
function handleResize() {
setWindowDimensions(getWindowDimensions())
}
window.addEventListener("resize", handleResize)
return () => window.removeEventListener("resize", handleResize)
}
}, [hasWindow])
return windowDimensions
}
const { height, width } = useWindowDimensions()
const breakpoint = 768
return (
<div>
{width <= breakpoint ? (
<div>
<HamburgerMenu />
</div>
) : (
<div>
<FullMenu />
</div>
)
}
export default Navbar
Is there a way I can force the Next.js app to apply the effects on the first render?
Share Improve this question edited Jul 2, 2020 at 13:51 theairbend3r asked Jul 2, 2020 at 9:22 theairbend3rtheairbend3r 7993 gold badges10 silver badges30 bronze badges2 Answers
Reset to default 6Next.js is universal, which means it executes code first server-side, then client-side. The window object is only present client-side
so in the first time it cannot access to window
, so value of windowDimensions
is null
when you resize you change the state make your app re-render , so it bee client-side-rendering => you can access to window , then your code work
Completing above answer, yes, with useEffect or useLayoutEffect, here is my own hook for this purpose:
import { useState, useEffect, useLayoutEffect, useCallback } from 'react';
import throttle from 'lodash/throttle';
type ScreenSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
interface ScreenDimension {
screenSize: ScreenSize | undefined;
width: number | undefined;
height: number | undefined;
}
function getScreenSize(width: number): ScreenSize {
if (width >= 1536) {
return '2xl';
} else if (width >= 1280) {
return 'xl';
} else if (width >= 1024) {
return 'lg';
} else if (width >= 768) {
return 'md';
} else if (width >= 640) {
return 'sm';
} else {
return 'xs';
}
}
export function useScreenSize(): ScreenDimension {
const [dimension, setDimension] = useState<ScreenDimension>({
screenSize: undefined, // Default to 'xs'
width: undefined,
height: undefined,
});
// Define the resize handler
const handleResize = useCallback(() => {
if (typeof window !== 'undefined') {
const newSize = getScreenSize(window.innerWidth);
setDimension({
screenSize: newSize,
width: window.innerWidth,
height: window.innerHeight,
});
}
}, []);
// Create a throttled version of the resize handler
const throttledHandleResize = useCallback(throttle(handleResize, 300), [
handleResize,
]);
useLayoutEffect(() => {
if (typeof window !== 'undefined') {
// Run the handler immediately to set the initial screen size
handleResize();
// Set up the throttled resize listener
window.addEventListener('resize', throttledHandleResize);
// Clean up
return () => {
throttledHandleResize.cancel(); // Cancel the throttle function on cleanup
window.removeEventListener('resize', throttledHandleResize);
};
}
}, [throttledHandleResize]);
return dimension;
}