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

Error: Cannot find module use-sync-external-store/shim/with-selector #379

Closed
sinnrrr opened this issue Jan 22, 2022 · 30 comments
Closed

Error: Cannot find module use-sync-external-store/shim/with-selector #379

sinnrrr opened this issue Jan 22, 2022 · 30 comments
Labels
bug Something isn't working

Comments

@sinnrrr
Copy link

sinnrrr commented Jan 22, 2022

Hello, thanks for such a great package!

I have some troubles with connecting web3-react to my application. I'm having the following error:

Error: Cannot find module '/Users/dmytro/Work/learn/web3-react/packages/example/node_modules/use-sync-external-store/shim/with-selector' imported from /Users/dmytro/Work/learn/web3-react/packages/example/node_modules/@web3-react/core/node_modules/zustand/esm/index.mjs
Did you mean to import use-sync-external-store/shim/with-selector.js?

Tried launching the official example under packages/example, but also got the same issue.

EDIT: I've detected, that it occurs when I'm destructuring from connector hooks (so the problem lies in initializeConnector function):

import { hooks } from "../connectors/metaMask";

const { useProvider } = hooks;
@NoahZinsmeister NoahZinsmeister added the bug Something isn't working label Jan 24, 2022
@NoahZinsmeister
Copy link
Contributor

hm, i haven't seen this before. undoubtedly it's related to the v4 of zustand, which introduces use-sync-external-store (see pmndrs/zustand#550)

@BennyH26
Copy link

Having the same problem, can't figure out a workaround

@earthtojake
Copy link

+1 having the same problem, works in the example but not in our implementation, can't find a work around

@michaelxuwu
Copy link

Ran into the same issue with Next.js. I found importing dynamically with no SSR (as in the example) solved this for me.

@sinnrrr
Copy link
Author

sinnrrr commented Jan 25, 2022

+1 having the same problem, works in the example but not in our implementation, can't find a work around

Did you try running example yourselves (on localhost)? It’s strange, because official example doesn’t work for me as well as my own implementation.

@BennyH26
Copy link

Ran into the same issue with Next.js. I found importing dynamically with no SSR (as in the example) solved this for me.

Using dynamic imports like the example fixed this problem for me. Thank you for the workaround until we can have a more robust solution!

@fjun99
Copy link

fjun99 commented Jan 29, 2022

Ran into the same issue with Next.js. I found importing dynamically with no SSR (as in the example) solved this for me.

Using dynamic imports like the example fixed this problem for me. Thank you for the workaround until we can have a more robust solution!

This works for me. bypass this error just like you.

@BowgartField
Copy link

BowgartField commented Feb 6, 2022

Is it possible to correct this issue ? this is very annoying @NoahZinsmeister

@mauver
Copy link

mauver commented Feb 7, 2022

This problem is still not working on build mode.

dynamic loading is working on "yarn dev" only.

how to solve it?

@mmoderwell
Copy link

@mauver @BowgartField I believe I've got a solution! Strange to see this work but in /node_modules/@web3-react/core/node_modules/zustand/esm/index.mjs you should see the first two lines as:

import { useDebugValue } from 'react';
import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/shim/with-selector';

Add a .js extension to line two so you have:

import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/shim/with-selector.js';

Never great editing stuff in `/node_modules/ but hopefully this works for ya'll as a temporary fix.

@pedroapfilho
Copy link

Maybe it's the version of Zustand used here? It should be added to the zustand library

@AzzouQ
Copy link

AzzouQ commented Feb 8, 2022

For those who use yarn, you can also add the following to your package.json without modifying anything in node_modules.

  "resolutions": {
    "zustand": "^3.6.9"
  }

It will force @web3-react to use the stable version of zustand instead of the beta (which is likely to cause the bug). Note that if you use another library that rely on zustand, that version will be used too.

With this or #379 (comment), there's a weird log in nextJS console:

error - ../src/index.ts (40:4) @ <unknown>
ReferenceError: window is not defined
null

@mmoderwell can you confirm this ? Or those are something on my own.

@sinnrrr
Copy link
Author

sinnrrr commented Feb 8, 2022

@AzzouQ it seems like web3-react does not handle SSR, that is why the components, that make use of web3-react are loaded dynamically without SSR.

@AzzouQ
Copy link

AzzouQ commented Feb 8, 2022

Indeed, Adding .js or using resolution work without dynamic but only in dev.
When trying to build with SSR or SSG, dynamic is required for the build to not fail...

@BowgartField
Copy link

mmoderwell

I already tried this but whitout sucess :/

@mauver
Copy link

mauver commented Feb 8, 2022

For those who use yarn, you can also add the following to your package.json without modifying anything in node_modules.

  "resolutions": {
    "zustand": "^3.6.9"
  }

It will force @web3-react to use the stable version of zustand instead of the beta (which is likely to cause the bug). Note that if you use another library that rely on zustand, that version will be used too.

With this or #379 (comment), there's a weird log in nextJS console:

error - ../src/index.ts (40:4) @ <unknown>
ReferenceError: window is not defined
null

@mmoderwell can you confirm this ? Or those are something on my own.

thank you all guys. I solved by adding the zustand package on my package.json.

@AzzouQ
Copy link

AzzouQ commented Feb 8, 2022

@sinnrrr It seems like it also occur when using getPriorityConnector and trying to call any returned hook in a component imported wihtout ssr: false. Did you find a proper workaround ? Or do we need to dynamic import all of our component (which is kinda annoying since most of my compenent will use those hooks)..

@amsheehan
Copy link

amsheehan commented Feb 8, 2022

Bump. How does the library's ONE dependency break? 😆

@AzzouQ
Copy link

AzzouQ commented Feb 8, 2022

From what I have understand, it's not related to zustand but to @web3-react and NextJS, since using a version of zustand that doesn't rely on use-sync-external-store makes the error disepear but prevent NextJS to correctly build.

@web3-react just doesn't support SSR, you won't have this error if using CRA I believe..

@amsheehan
Copy link

Right. But web3-react takes that version of zustand as a dependency. Even when not doing any SSR the library is unusable in NextJS.

If that is the intended behavior, the library should be renamed to @web3-react-but-not-for-nextjs or something. 😄

@sinnrrr
Copy link
Author

sinnrrr commented Feb 9, 2022

@amsheehan please, look at the official example, it uses nextjs, you can try to launch it and look at source code

@AzzouQ
Copy link

AzzouQ commented Feb 9, 2022

I'm actually using some workaround to be able to use those hooks without the inconvenience of importing everything dynamicaly. I've no idea how bad practice it can be tho or what drawback it can have (bad SEO etc etc...), but it seems to work as for now...

The main idea is to dynamic import only one component that will deconstruct all your hooks (from initializeConnector or getPriorityConnector). This component will act as a Provider and store those hooks inside redux (or zustand) to make them available everywhere without the need to dynamic import any other component. It fix the Error: Cannot find module use-sync-external-store/shim/with-selector and works with SSR in production.

Will look like this:

// HooksProvider.tsx

const HooksProvider: FC = ({ children }) => {
  const dispatch = useAppDispatch();
  const [isReady, setReady] = useState<boolean>(false);

  const priorityConnector = getPriorityConnector(
    [metaMask, metaMaskHooks],
    [walletConnect, walletConnectHooks],
    [network, networkHooks]
  );

  useEffect(() => {
    dispatch(hooksActions.init({ priorityConnector }));
    setReady(true);
  }, [dispatch, priorityConnector]);

  return isReady ? (children as ReactElement) : null;
};

export default HooksProvider;
// hooks.slice.ts

import type { PriorityConnector } from 'src/connectors';

export type HooksState = {
  priorityConnector: PriorityConnector | undefined;
};

const initialState: HooksState = {
  priorityConnector: undefined,
};

const hooksSlice = createSlice({
  name: 'hooks',
  initialState: initialState,
  reducers: {
    init: (_state, action: PayloadAction<HooksState>) => action.payload,
    reset: () => initialState,
  },
});

const stateSelector = createSelector(
  ({ hooks }: { hooks: HooksState }) => hooks,
  (state) => state
);

export const hooksSelectors = {
  getPriorityConnector: createSelector(
    stateSelector,
    (state) => state.priorityConnector as PriorityConnector
  ),
};

export const { actions: hooksActions, reducer: hooksReducer } = hooksSlice;
// pages/index.tsx (could probably be added in _app.tsx with other Provider)

const HooksProvider = dynamic(
  () => import('HooksProvider'),
  { ssr: false }
);

const Home: NextPage = () => {
  return (
    <>
      <Head>
        {...}
      </Head>
      <HooksProvider>
        {...} //  Your app (Header, Body, Footer, or whatever)
      </HooksProvider>
    </>
  );
};

export const getServerSideProps: GetServerSideProps = async ({ locale }) => {
  return {
    props: { ... }, 
  };
};

export default Home;

Then you can use it like this in any children of your Provider

export const Component: FC = () => {
  const { usePriorityConnector } = useAppSelector(
    prioritySelectors.getPriorityConnector
  );
  const connector = usePriorityConnector();

  return  (<Text>{getName(connector)}</Text>);
};

2 improvements could be done with that solution:

  • Find a way to dynamicaly import a custom hook without SSR, like this, Provider won't be needed at all.
  • Create a initialState with all hooks with default value (false or () => undefined) instead of undefined, like this we won't need to conditionally render null or children in the provider.

@amsheehan
Copy link

Another solution without having to change the approach to loading components is to override the dependency.

This package forces resolution to a specified version in package-lock.json: https://www.npmjs.com/package/npm-force-resolutions

In your main package.json you can then add the following:

"scripts": {
  "preinstall": "npx npm-force-resolutions"
},
"resolutions": {
  "zustand": "3.6.9"
}

@whale-guru-dev
Copy link

Another solution without having to change the approach to loading components is to override the dependency.

This package forces resolution to a specified version in package-lock.json: https://www.npmjs.com/package/npm-force-resolutions

In your main package.json you can then add the following:

"scripts": {
  "preinstall": "npx npm-force-resolutions"
},
"resolutions": {
  "zustand": "3.6.9"
}

Did you try build?

ReferenceError: window is not defined

Because of this above error, building can't be done successfully.

@pedroapfilho
Copy link

So, the solution would be to create a PR here to:

  • Update zustand
  • Add checks for window

That's it?

@pedroapfilho
Copy link

This fixes it: #415

The only thing that happens now is the ReferenceError: window is not defined that doesn't actually break the build.

@AzzouQ
Copy link

AzzouQ commented Feb 10, 2022

It's never been a problem with Zustand in the first place (see #379 (comment)). Importing component dynamically is enough for not having the zustand error, modifying the version won't solve the build problem since it occur with resolutions in package.json.

The problem is related to NextJS especially(see #379 (comment)), as this doesn't occur in CRA or else.

zzmp added a commit to zzmp/zustand that referenced this issue Feb 24, 2022
Imports `use-sync-external-store/shim/with-selector.js` (with a `.js` suffix) so that it is correctly resolved by esm bundlers like nextjs.
Fixes Uniswap/web3-react#379, which is broken using [4.0.0-beta.1](https://www.npmjs.com/package/zustand/v/4.0.0-beta.1).
@montanaflynn
Copy link

montanaflynn commented Feb 25, 2022

Just hit this as well, it would be nice if the example project included the fix for dynamic import so people didn't try it out and have to find this issue. Here's how I "fixed" the error:

- import MetaMaskCard from "../components/connectors/MetaMaskCard";
+ import dynamic from "next/dynamic";
+ const MetaMaskCard = dynamic(
+   () => import("../components/connectors/MetaMaskCard"),
+   { ssr: false }
+ );

@zzmp zzmp mentioned this issue Feb 25, 2022
@NoahZinsmeister
Copy link
Contributor

this issue should be fixed by #438 , released under beta tags for all packages

@AlexandruMares
Copy link

the issue is not fixed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.