skip to Main Content

Facing while experimenting with device orientation

Here is the code, I am using

import React, { useState, useEffect } from 'react';

 export default function App() {
   const [isLandscape, setIsLandscape] = useState(window.innerWidth > window.innerHeight);

 useEffect(() => {
  function handleResize() {
  setIsLandscape(window.innerWidth > window.innerHeight);
  }

  window.addEventListener('resize', handleResize);
  return () => window.removeEventListener('resize', handleResize);
 }, []);

 return (
   <div>
  {isLandscape ? <Landscape /> : <Portrait />}
   </div>
);
}

And for your reference, here is the last and related question, I posted on SO

Calling functions as per device orientation

Experiment # 1

if (typeof window !== "undefined") {

        const [isLandscape, setIsLandscape] = useState(window.innerWidth > window.innerHeight);

....

}

getting :

This resolved that issue, but showing the new one

Expected server HTML to contain a matching <div> in <body>.

Final code

export default function App() {
        
    if (typeof window !== "undefined") {

        const [isLandscape, setIsLandscape] = useState(window.innerWidth > window.innerHeight);

         useEffect(() => {
          function handleResize() {
            setIsLandscape(window.innerWidth > window.innerHeight);
          }

          window.addEventListener('resize', handleResize);
          return () => window.removeEventListener('resize', handleResize);
         }, []);

         return (
           <div>
            {isLandscape ? <Landscape /> : <Portrait />}
           </div>
        );
        
    }
        
}

Complete error says:

Error: Hydration failed because the initial UI does not match what was rendered on the server.
See more info here: https://nextjs.org/docs/messages/react-hydration-error

2

Answers


  1. If you are using app router, add "use client" at the top to tell it it’s a client component so it has access to window.

    Login or Signup to reply.
  2. The part of your code that is actually causing the issue is the useState(window.innerWidth > window.innerHeight). That is run in both the server and the client and is what is giving you the "window is not defined" error.

    Your approach to wrapping everything in the typeof window !== "undefined" check doesn’t work because it does not return anything at all when run server side. It is theoretically possible to make that approach work by adding a return value when it is server-side, but there is a better solution:

    1. Set a hard-coded default value for isLandscape to allow for it to run server side
    2. Move setting the real initial value for the client to the useEffect handler. useEffect is only run client side so you can access window here without issue.

    By doing this, we’ve moved all the code that needs the window to client side code, and set appropriate defaults so the page can run server side while still returning a component as expected.

    export default function App() {
        const [isLandscape, setIsLandscape] = useState(true); // <-- Changed to just true, you can use false if you prefer.
    
        useEffect(() => {
            function handleResize() {
                setIsLandscape(window.innerWidth > window.innerHeight);
            }
            handleResize(); // <-- Added, sets initial value right away once loaded in the browser
    
            window.addEventListener('resize', handleResize);
            return () => window.removeEventListener('resize', handleResize);
        }, []);
    
        return <div>{isLandscape ? <Landscape /> : <Portrait />}</div>;
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search