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: Hook can only be invoked from render methods. #3308

Closed
tw1t611 opened this issue Nov 6, 2021 · 23 comments · Fixed by #3344
Closed

Error: Hook can only be invoked from render methods. #3308

tw1t611 opened this issue Nov 6, 2021 · 23 comments · Fixed by #3344
Labels

Comments

@tw1t611
Copy link

tw1t611 commented Nov 6, 2021

Describe the bug
I upgraded to nextjs v12 and got this error:
Error: Hook can only be invoked from render methods.
image
The error gets thrown on every other react hook as well.

To Reproduce

store.js

import { createContext } from "preact";
import { useReducer, useContext } from "preact/hooks";

const reducer = (state, action) => {
  switch (action.type) {
    case "filter":
      return { ...state, filter: action.data };
    default:
      return;
  }
};

const initialState = {
  filter: { category: null },
};
const StoreContext = createContext(initialState);

export function StoreProvider(props) {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <StoreContext.Provider value={{ state, dispatch }}>
      {props.children}
    </StoreContext.Provider>
  );
}

export function useStore() {
  const store = useContext(StoreContext);

  return store;
}

Steps to reproduce the behavior:

  1. Add store.js to a nextjs v12 project
  2. See error

Expected behavior
The code should just work as before.

Additional Info
package.json

{
  "private": true,
  "license": "The Unlicense",
  "scripts": {
    "dev": "npx netlify-cms-proxy-server & next dev",
    "build": "next build",
    "start": "next start",
    "postbuild": "next-sitemap"
  },
  "dependencies": {
    "framer-motion": "^5.2.1",
    "next": "^12.0.3",
    "next-mdx-remote": "^3.0.7",
    "next-plugin-preact": "^3.0.6",
    "next-pwa": "^5.4.0",
    "preact": "^10.5.14",
    "preact-render-to-string": "^5.1.19",
    "react": "npm:@preact/compat",
    "react-dom": "npm:@preact/compat",
    "react-intersection-observer": "^8.32.2",
    "react-lazy-hydration": "^0.1.0",
    "react-responsive": "^9.0.0-beta.4",
    "react-ssr-prepass": "npm:preact-ssr-prepass"
  },
  "devDependencies": {
    "@tailwindcss/forms": "^0.4.0-alpha.1",
    "@tailwindcss/typography": "^0.5.0-alpha.2",
    "autoprefixer": "^10.4.0",
    "eslint": "<8.0.0",
    "eslint-config-next": "12.0.3",
    "gray-matter": "^4.0.3",
    "netlify-cms-proxy-server": "^1.3.22",
    "next-sitemap": "^1.6.203",
    "postcss": "^8.3.11",
    "postcss-flexbugs-fixes": "^5.0.2",
    "postcss-preset-env": "^6.7.0",
    "sharp": "^0.29.2",
    "tailwindcss": "^3.0.0-alpha.1"
  }
node --version
v16.11.0
@tw1t611
Copy link
Author

tw1t611 commented Nov 18, 2021

Hey, could you give an update please about what the problem is and if you already had time to work on it?

@JoviDeCroock
Copy link
Member

JoviDeCroock commented Nov 18, 2021

Well, I've been debugging a similar issue on Next. Adding getServerSideProps to the page made it work which showed the issue that Next has stopped deduping dependencies when it attempts to statically prerender. vercel/next.js#31538

@tw1t611
Copy link
Author

tw1t611 commented Nov 18, 2021

Thank you very much. Guess I can close this now and follow the nextjs issue. :)

@tw1t611 tw1t611 closed this as completed Nov 18, 2021
@JoviDeCroock
Copy link
Member

Oh feel free to keep it open, you can test whether this applies to you as well by adding getServerSideProps

@tw1t611 tw1t611 reopened this Nov 18, 2021
@tw1t611
Copy link
Author

tw1t611 commented Nov 27, 2021

Hey, just upgraded to next 12 again. Seems like there is a problem with the compat exports now:

Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './compat/package.json' is not defined by "exports" in /home/user/Projects/bestnotebook-tech/node_modules/preact/package.json
    at new NodeError (node:internal/errors:371:5)
    at throwExportsNotFound (node:internal/modules/esm/resolve:440:9)
    at packageExportsResolve (node:internal/modules/esm/resolve:692:3)
    at resolveExports (node:internal/modules/cjs/loader:482:36)
    at Function.Module._findPath (node:internal/modules/cjs/loader:522:31)
    at Function.Module._resolveFilename (node:internal/modules/cjs/loader:919:27)
    at Function.mod._resolveFilename (/home/user/Projects/bestnotebook-tech/node_modules/next/dist/build/webpack/require-hook.js:171:28)
    at Function.Module._resolveFilename (/home/user/Projects/bestnotebook-tech/node_modules/module-alias/index.js:49:29)
    at Function.resolve (node:internal/modules/cjs/helpers:108:19)
    at getPackagePath (/home/user/Projects/bestnotebook-tech/node_modules/next/dist/build/webpack-config.js:543:41) {
  code: 'ERR_PACKAGE_PATH_NOT_EXPORTED'
}
error Command failed with exit code 1.

package.json

{
  "private": true,
  "license": "The Unlicense",
  "scripts": {
    "dev": "npx netlify-cms-proxy-server & next dev",
    "build": "next build",
    "start": "next start",
    "postbuild": "next-sitemap"
  },
  "dependencies": {
    "framer-motion": "^5.3.3",
    "next": "^12.0.4",
    "next-mdx-remote": "^3.0.7",
    "next-plugin-preact": "^3.0.6",
    "next-pwa": "^5.4.1",
    "preact": "^10.6.1",
    "preact-render-to-string": "^5.1.19",
    "react": "npm:@preact/compat",
    "react-dom": "npm:@preact/compat",
    "react-intersection-observer": "^8.32.5",
    "react-lazy-hydration": "^0.1.0",
    "react-responsive": "^9.0.0-beta.5",
    "react-ssr-prepass": "npm:preact-ssr-prepass"
  },
  "devDependencies": {
    "@tailwindcss/forms": "^0.4.0-alpha.1",
    "@tailwindcss/typography": "^0.5.0-alpha.2",
    "autoprefixer": "^10.4.0",
    "eslint": "8.3.0",
    "eslint-config-next": "12.0.4",
    "gray-matter": "^4.0.3",
    "netlify-cms-proxy-server": "^1.3.22",
    "next-sitemap": "^1.6.203",
    "postcss": "^8.4.3",
    "postcss-flexbugs-fixes": "^5.0.2",
    "postcss-preset-env": "^7.0.1",
    "sharp": "^0.29.2",
    "tailwindcss": "^3.0.0-alpha.1"
  }
}

@JoviDeCroock
Copy link
Member

Will be in the next release

@developit developit linked a pull request Nov 29, 2021 that will close this issue
@JoviDeCroock
Copy link
Member

@tw1t611 this has been published in 10.6.2

@JoviDeCroock
Copy link
Member

I'll close this for now, feel free to comment in case there are more issues

@tw1t611
Copy link
Author

tw1t611 commented Dec 12, 2021

Hey, came up again. :(

package.json

{
  "private": true,
  "license": "The Unlicense",
  "scripts": {
    "dev": "npx netlify-cms-proxy-server & next dev",
    "build": "next build",
    "start": "next start",
    "postbuild": "next-sitemap"
  },
  "dependencies": {
    "framer-motion": "^5.3.3",
    "next": "^12.0.5",
    "next-mdx-remote": "^3.0.7",
    "next-plugin-preact": "^3.0.6",
    "next-pwa": "^5.4.1",
    "preact": "^10.6.2",
    "preact-render-to-string": "^5.1.19",
    "react": "npm:@preact/compat",
    "react-dom": "npm:@preact/compat",
    "react-intersection-observer": "^8.32.5",
    "react-lazy-hydration": "^0.1.0",
    "react-responsive": "^9.0.0-beta.5",
    "react-ssr-prepass": "npm:preact-ssr-prepass",
    "scheduler": "^0.20.2"
  },
  "devDependencies": {
    "@tailwindcss/forms": "^0.4.0-alpha.1",
    "@tailwindcss/typography": "^0.5.0-alpha.2",
    "autoprefixer": "^10.4.0",
    "eslint": "8.4.1",
    "eslint-config-next": "12.0.7",
    "gray-matter": "^4.0.3",
    "netlify-cms-proxy-server": "^1.3.22",
    "next-sitemap": "^1.6.203",
    "postcss": "^8.4.3",
    "postcss-flexbugs-fixes": "^5.0.2",
    "postcss-preset-env": "^7.0.1",
    "sharp": "^0.29.2",
    "tailwindcss": "^3.0.0-alpha.1"
  }
}

@tw1t611
Copy link
Author

tw1t611 commented Jan 7, 2022

@JoviDeCroock Hey, I am a little late, but I checked the issue again and it still isn't working for me.

Running yarn upgrade --latest got me the newest version (10.6.4).
After running yarn dev, the first error occured again.

Error: Cannot find module 'scheduler/package.json'

After installing the scheduler package, the error described above came up again.

Do you need more info for reproduction?

{
  "private": true,
  "license": "The Unlicense",
  "scripts": {
    "dev": "npx netlify-cms-proxy-server & next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "postbuild": "next-sitemap"
  },
  "dependencies": {
    "amazon-product-api": "^0.4.4",
    "framer-motion": "^5.5.6",
    "next": "^12.0.7",
    "next-mdx-remote": "^3.0.7",
    "next-plugin-preact": "^3.0.6",
    "next-pwa": "^5.4.1",
    "preact": "^10.6.4",
    "preact-render-to-string": "^5.1.19",
    "react": "npm:@preact/compat",
    "react-dom": "npm:@preact/compat",
    "react-intersection-observer": "^8.32.5",
    "react-lazy-hydration": "^0.1.0",
    "react-responsive": "^9.0.0-beta.5",
    "react-ssr-prepass": "npm:preact-ssr-prepass",
    "tailwindcss-elevation": "^1.0.1"
  },
  "devDependencies": {
    "@tailwindcss/forms": "^0.4.0-alpha.1",
    "@tailwindcss/typography": "^0.5.0-alpha.2",
    "autoprefixer": "^10.4.2",
    "eslint": "^8.6.0",
    "eslint-config-next": "12.0.7",
    "gray-matter": "^4.0.3",
    "netlify-cms-proxy-server": "^1.3.23",
    "next-sitemap": "^1.6.245",
    "postcss": "^8.4.5",
    "postcss-flexbugs-fixes": "^5.0.2",
    "postcss-preset-env": "^7.2.0",
    "sharp": "^0.29.2",
    "tailwindcss": "^3.0.12"
  }
}

@herrKlein
Copy link

got the same error when adding unistore to a preact project

@benzizoo
Copy link

happens to me as well while using react-hook-form and Next js ^v12.x

@teodragovic
Copy link
Contributor

teodragovic commented Jan 14, 2022

Happens to me as well. Here is minimal repro (using create-next-app)

// package.js
  "dependencies": {
    "next": "12.0.8",
    "next-plugin-preact": "^3.0.6",
    "preact": "^10.6.4",
    "preact-render-to-string": "^5.1.19",
    "react-ssr-prepass": "npm:preact-ssr-prepass@^1.2.0",
    "react": "npm:@preact/compat@^17.0.3",
    "react-dom": "npm:@preact/compat@^17.0.3",
    "react-redux": "^7.2.6",
    "redux": "^4.1.2"
  },
  "devDependencies": {
    "@types/node": "17.0.8",
    "@types/react": "17.0.38",
    "eslint": "8.6.0",
    "eslint-config-next": "12.0.8",
    "scheduler": "^0.20.2",
    "typescript": "4.5.4"
  }
// src/store.js
import { createStore, combineReducers } from 'redux';

const appReducer = combineReducers({});

const reducer = (state, action) => {
    return appReducer(state, action);
};

const store = createStore(reducer, {});

export default store;
// src/pages/_app.js

import { h } from 'preact';
import { Provider } from 'react-redux';

import store from '../store';

const MyApp = ({ Component, pageProps }) => {

    const getLayout = Component.getLayout || ((page) => page);
    return (
        <Provider store={ store }>
            { getLayout(<Component { ...pageProps } />) }
        </Provider>
    );
};

export default MyApp;
// src/pages/index.js
import { h } from 'preact';
import { useState } from 'preact/hooks';

const Login = () => {
    const [ counter ] = useState(1);
    return (
        <div>hello {couter}</div>
    );
};
const LoginLayout = ({ children }) => <article>{ children }</article>

export default Login;

Login.getLayout = function getLayout(page) {
    return (
        <LoginLayout>
            { page }
        </LoginLayout>
    );
};

@teodragovic
Copy link
Contributor

FWIW I bumped down next to v11.1.3 and error is gone

@scheatkode
Copy link

As a workaround for anyone bumping into this issue, try importing from react instead of preact/hooks.

import { useReducer, useState } from 'react'

Hoping this helps.

@acolle
Copy link

acolle commented Jul 12, 2022

Has anyone found a clear fix for this issue?

I tried @scheatkode solution but the preact/debug package stills shows the Uncaught Error: Hook can only be invoked from render methods.

I'm trying to build and deploy my Preact app via Docker with Nginx.

// package.json
"scripts": {
    "build": "preact build",
    "np-build": "preact build --no-prerender",
    "serve": "sirv build --port 8080 --cors --single",
    "dev": "preact watch",
    "lint": "eslint src",
    "test": "jest"
  },
  "eslintConfig": {
    "extends": "preact",
    "ignorePatterns": [
      "build/"
    ]
  },
  "devDependencies": {
    "autoprefixer": "^9.8.8",
    "enzyme": "^3.10.0",
    "enzyme-adapter-preact-pure": "^2.0.0",
    "eslint": "^7.5.1",
    "eslint-config-preact": "^1.1.0",
    "jest": "^27.3.1",
    "jest-preset-preact": "^4.0.5",
    "postcss": "^7.0.39",
    "sirv-cli": "1.0.3",
    "tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.17"
  },
  "dependencies": {
    "history": "^5.0.1",
    "preact": "^10.3.2",
    "preact-cli": "^0.1.0",
    "preact-render-to-string": "^5.1.4",
    "preact-router": "^3.2.1",
    "react": "^18.2.0",
    "webpack": "^4.46.0"
  },
  "jest": {
    "preset": "jest-preset-preact",
    "setupFiles": [
      "<rootDir>/tests/__mocks__/browserMocks.js",
      "<rootDir>/tests/__mocks__/setupTests.js"
    ]
  }

And my useReducer hook

// AuthProvider.js

import { h } from 'preact';
//import { useEffect, useReducer, useState } from 'preact/hooks';
import { useEffect, useReducer, useState } from 'react';
import { route } from 'preact-router';

import AuthContext from '../context/AuthContext';
import authReducer from '../reducers/auth';
import { activeSession, startLogout } from '../actions/authentication';
import { getCurrentUser } from '../actions/users';
import { getLocalStorage, checkValidSession } from '../utils/localStorage';

const AuthProvider = (props) => {

  const [ state, dispatch ] = useReducer(authReducer, {});
  const [ stateReady, setStateReady ] = useState(false);

  const checkActiveSession = async () => {

    if (!state.isLoggedIn) {
      let token = state.token;
      if (!token) {
        const localToken = getLocalStorage('token');
        if (localToken) {
          token = localToken;
        }
      }

      if (token) {
        try {
          const result = await getCurrentUser(token);
          if (result.type === 'ACTIVE') {
            dispatch(result);
          } else {
            logout();
          }
        } catch (err) {
          logout();
        }
      } else {
        logout();
      }
    }

    setStateReady(true);
  }

  const logout = async () => {
    const result = await startLogout();
    if (result.type === 'LOGOUT') {
      dispatch(result);
      route('/auth', true);
    }
  }

  useEffect(() => {
    checkActiveSession();
  }, []);

  return (
    stateReady && (
      <AuthContext.Provider value={{ state, dispatch }}>
        {props.children}
      </AuthContext.Provider>
    )
  )
}

export default AuthProvider;

@scheatkode
Copy link

Hey @acolle, the workaround works because of preact/compat being linked to react. Just install the following packages to make it work react@npm:@preact/compat and react-dom@npm:@preact/compat.

Hoping this helps

@acolle
Copy link

acolle commented Jul 12, 2022

Thanks for the clarification @scheatkode.
I installed the react@npm:@preact/compat and react-dom@npm:@preact/compat packages but the issue remained for me.

In the end, I got it working by installing the latest versions of preact and preact-cli. I must have fallen a bit behind regarding the latest releases.

Thanks for your quick reply

@Ethaan
Copy link

Ethaan commented Jul 25, 2022

vercel/next.js#31538

Did you find a solution?

@uxtechie
Copy link

uxtechie commented Aug 3, 2022

maybe this is a general workaround?

example error:
ERR! Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './package.json' is not defined by "exports" in node_modules/.pnpm/@storybook+ui@6.5.9_dmmjpkrptnkrz2kzqikv5vt4mi/node_modules/react/package.json

solved:

nano node_modules/.pnpm/@storybook+ui@6.5.9_dmmjpkrptnkrz2kzqikv5vt4mi/node_modules/react/package.json

just add "./package.json": "./package.json" to module exports declaration

{
  "public": true,
  "name": "@preact/compat",
  "version": "17.1.1",
  "description": "Alias of preact/compat",
  "main": "./index.js",
  "module": "./index.mjs",
  "exports": {
    ".": {
      "module": "./index.mjs",
      "import": "./index.mjs",
      "require": "./index.js"
    },
    "./server": {
      "module": "./server.mjs",
      "import": "./server.mjs",
      "require": "./server.js"
    },
    "./server.browser": {
      "module": "./server.mjs",
      "import": "./server.mjs",
      "require": "./server.js"
    },
    "./jsx-dev-runtime": {
      "module": "./jsx-dev-runtime.mjs",
      "import": "./jsx-dev-runtime.mjs",
      "require": "./jsx-dev-runtime.js"
    },
    "./jsx-runtime": {
      "module": "./jsx-runtime.mjs",
      "import": "./jsx-runtime.mjs",
      "require": "./jsx-runtime.js"
    },
    "./package.json": "./package.json"
  },
  "repository": "preactjs/compat-alias-package",
  "author": "Preact Team <team@preactjs.com>",
  "license": "MIT",
  "homepage": "https://preactjs.com",
  "peerDependencies": {
    "preact": "*"
  },
  "devDependencies": {
    "preact": "^10.5.15"
  }
}

My question: is a good idea add this to the official react/compat package or is a mistaken config?

@drager
Copy link

drager commented Oct 13, 2022

Any news about this? Just created a new next.js project with latest preact and I get this error :(

EDIT: Works fine if I add experimental: {esmExternals: false} to my next.config.js

@lukenems
Copy link

lukenems commented Nov 1, 2022

using Preact@10.11.2 + Vite@3.2.0

I'm getting this same error when trying to upload multiple images to firebase storage, everything was working fine: I added state to a custom hook for holding return value from getDownloadURL(). Some code for reference:

import { useState } from "preact/hooks";
import { storage } from '../firebase';
import { getDownloadURL, ref, uploadBytes } from 'firebase/storage';

export const useUploadImages =  (files, name) => {
  const [imagePath, setImagePath] = useState([]);
  const images = [...files];

  images.forEach(image => {
    const imageRef = ref(storage, `Client_Images/${name}/${image.name}`);
    uploadBytes(imageRef, files)
      .then((snapshot) => {
        getDownloadURL(snapshot.ref)
          .then((url) => {
            setImagePath((prev) => [...prev, url]);
          })
        })
      })
  return imagePath;
}

@rhengles
Copy link

I'm having this problem as described here: preactjs/preact-ssr-prepass#48
It appears to only happen on SSR. Maybe comp only exists on the client ? @JoviDeCroock

// preact/debug/src/debug.mjs line 255
	options._hook = (comp, index, type) => {
		if (!comp || !hooksAllowed) {
			throw new Error('Hook can only be invoked from render methods.');
		}

		if (oldHook) oldHook(comp, index, type);
	};

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

Successfully merging a pull request may close this issue.