uidotdev / usehooks

A collection of modern, server-safe React hooks – from the ui.dev team
https://usehooks.com
MIT License
9.52k stars 500 forks source link

Error: useLocalStorage is a client-only hook #318

Open Nusab19 opened 3 months ago

Nusab19 commented 3 months ago

I'm using useLocalStorage hook from @uidotdev/usehooks. Even though HomePage.tsx file is set to "use client", I'm getting the following error:

 ⨯ Error: useLocalStorage is a client-only hook
    at HomePage (./src/components/HomePage.tsx:15:98)
digest: "1786536127"
 GET / 500 in 180ms

Even though the readme states that the hooks are "server-safe", I keep getting this error.

The minimal reproduceable code can be found at: https://github.com/Nusab19/mwc-useLocalStorageHook

The responsible code:

"use client";
import { useLocalStorage } from "@uidotdev/usehooks";
import { Button } from "./ui/button";
import { Separator } from "./ui/separator";

const HomePage = () => {
  const [count, setCount] = useLocalStorage("count", 0); // here's the problem

  return (
    <div className="...">
      <header className="...">Home Page</header>
      <Separator />
      <header className="...">Count: {count}</header>

      <div className="...">
        <Button onClick={() => setCount((prev) => prev + 1)}>Increment</Button>
        <Button onClick={() => setCount((prev) => prev - 1)}>Decrement</Button>
        <Button onClick={() => setCount(0)}>Reset</Button>
      </div>
    </div>
  );
};

export default HomePage;

I also posted a question in stackoverflow

ThePythoniousGuy commented 3 months ago

Had the same issue in my project. Couldn't find a solution.

So I wrote my own implementation of the useLocalStorage hook. It uses the default value at first. And a useEffect then changes the state to the local storage's value.

The problem with it is, it gives a flicker each time you refresh the page.

GeekPress commented 3 months ago

Same issue here when we refresh the page with ctrl+r :/

Nusab19 commented 3 months ago

Same issue here when we refresh the page with ctrl+r :/

I have identified the issue. The thing is, localSorage is only available in the client side.

But next.js tries to call it from server ( idk why ). That's the reason it is throwing the error.

I don't understand why would next.js call it from server side when I explicitly made the component "use client".

Here's the code that is raising the error. image

iamriot-dev commented 3 months ago

I don't understand why would next.js call it from server side when I explicitly made the component "use client".

"use client" does not mean that the component will not run on the server, SSR will still happen unless specifically disabled, this is intended behaviour. You can look into methods of disabling SSR for specific components on Next.js, if that is what you want to do.

The issue here is that useLocalStorage isn't "server safe", exactly because of the code you screenshotted. According to React docs, that function should be returning the value to be used during server rendering (instead of throwing an error), but this isn't possible since localStorage cannot be accessed by the server (the data is stored only on the browser). The only alternative is to use a fallback value (such as 0 in your case) on the server, which will differ from the actual value stored in the browser.

Unfortunately, the flicker that the alternative implementations have is largely unavoidable, for this very same reason.

hyper-dot commented 2 months ago

You can use dynamic imports. https://nextjs.org/learn-pages-router/seo/improve/dynamic-import-components#:~:text=import%20dynamic%20from%20%27next/dynamic%27%3B

twoguyslabs commented 2 months ago

I switch to useLocalStorageState from alibaba/hooks solve the problem