Skip to content
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

Remix + Vite unable to use client only libs #9019

Closed
yarapolana opened this issue Mar 8, 2024 · 7 comments
Closed

Remix + Vite unable to use client only libs #9019

yarapolana opened this issue Mar 8, 2024 · 7 comments

Comments

@yarapolana
Copy link

Reproduction

I am using a client hooks lib and everything works fine without vite, once setting up vite I get the error of "... is a client-only hook". I tried adding the lib to optimizeDeps same issue which is strange.
Tried using _.client.ts no luck.
Tried ssr: { external: 'client-lib' } no luck either.

Here is a simple repo with the initial settings.
There you can see the configuration for vite with the lib, and on root is where I am using the lib.

https://github.com/yarapolana/remix-vite-client-libs

System Info

System:
    OS: macOS 13.4
    CPU: (16) x64 Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz
    Memory: 1.23 GB / 64.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 20.11.0 - ~/.nvm/versions/node/v20.11.0/bin/node
    Yarn: 1.22.21 - ~/.nvm/versions/node/v20.11.0/bin/yarn
    npm: 10.2.4 - ~/.nvm/versions/node/v20.11.0/bin/npm
    pnpm: 8.15.4 - ~/.nvm/versions/node/v20.11.0/bin/pnpm
    Watchman: 2023.12.04.00 - /usr/local/bin/watchman
  Browsers:
    Brave Browser: 122.1.63.162
    Chrome: 121.0.6167.139
    Safari: 16.5
  npmPackages:
    @remix-run/dev: ^2.7.1 => 2.8.1 
    @remix-run/node: ^2.7.1 => 2.8.1 
    @remix-run/react: ^2.7.1 => 2.8.1 
    @remix-run/serve: ^2.7.1 => 2.8.1 
    vite: ^5.1.0 => 5.1.5

Used Package Manager

yarn

Expected Behavior

Ability to use client only libs without issues.

Actual Behavior

Error with "... is a client only hook" and no steps to fix it.

@franklinjavier
Copy link

I am experiencing a similar error, but I am using the "module" folder approach. The module has several folders, each with its own exported index.ts file. Before Vite, I was able to import everything from '~/modules/foo', but now I can't.

image

Is there a way to solve this "issue" without creating a separate folder called .server?

franklinjavier/remix-starter-template#75

@brophdawg11
Copy link
Contributor

From a quick glance, it looks like this library uses useSyncExternalStore to detect "server" which also means that it will detect "server" during the initial hydration render on the client - which makes sense to avoid hydration issues. That also explains why you still see the error when moving it into a .client folder. That avoids the issue during SSR but still tries to use the hook during the initial hydration render and the useSyncExternalStore snapshot triggers the error.

Error: useNetworkState is a client-only hook
    at getNetworkStateServerSnapshot 
    at Object.useSyncExternalStore 
    ...

Instead, you want to avoid running this hook until after hydration - which you can do with the remix-utils ClientOnly component:

import { useNetworkState } from "@uidotdev/usehooks";
import { ClientOnly } from "remix-utils/client-only";

export default function Index() {
  return (
    <div>
      <NetworkState />
      <ClientOnly>{() => <NetworkState />}</ClientOnly>
    </div>
  );
}

function NetworkState() {
  return <pre>{JSON.stringify(useNetworkState())}</pre>;
}

@brophdawg11 brophdawg11 closed this as not planned Won't fix, can't repro, duplicate, stale Mar 8, 2024
@franklinjavier
Copy link

@brophdawg11 should I open a new issue or discussion about my doubt?

@brophdawg11
Copy link
Contributor

I would see if the vite-env-only solution solves it via https://remix.run/docs/en/main/future/vite#splitting-up-client-and-server-code - and if not, yeah, please open a new issue for that since it's not related to this specific hook use-case.

@yarapolana
Copy link
Author

@brophdawg11 how would you use hook in this case? Can you elaborate before closing?
would you add let network = useNetworkState() inside stringify

@brophdawg11
Copy link
Contributor

brophdawg11 commented Mar 8, 2024

You can call a hook anywhere you want in a compnent as long as it's not called conditionally. I put it in stringify here just for the simple example, most folks would call it at the top of the function so it's more obvious:

function NetworkState() {
  let network = useNetworkState()
  return <pre>{JSON.stringify(network)}</pre>;
}

@yarapolana
Copy link
Author

Thanks @brophdawg11
I'll add this for future references

// using with context
import { createContext } from 'react'
import { ClientOnly } from 'react-utils/client-only' <-- v7.0.0-pre.7
import { useClientHook } from 'lib-with-probs'

const Context = createContext({})

function ForContext({ children }) {
  const foo = useClientHook()

  // To test I just added divs

  return (
    <div>
      {foo ? <div>its on</div> : <div>its off</div>}

      {children}
    </div>
  )
}

export const ContextProvider = ({ children }) => (
  <Context.Provider>
    <ClientOnly fallback={children}>
      {() => <ForContext>{children}</ForContext>}
    </ClientOnly>
  <Context.Provider>
)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants