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

Hooks not working correctly using off-the-shelf htm@^3.1.0 and preact@^10.5.14 #209

Open
integryiosama opened this issue Jul 25, 2021 · 4 comments

Comments

@integryiosama
Copy link

I am using htm and Preact as follows inside a simple functional component. This component is imported by a class component where it is swapped out according to a switching logic with another component. Here is how I am importing the libraries:

import { html } from 'htm/preact';
import { useEffect, useRef, useState } from 'preact/hooks';

I have the latest installed versions of each package. Hooks appear to work fine on first mount, but when the component unmounts, any version of Preact past 10.2.0 is failing with the message TypeError: Cannot read property '__hooks' of undefined. If I install 10.2.0 explicitly then this error goes away. I assume it is because htm has this version in the devDependencies:

  "devDependencies": {
    "@babel/core": "^7.2.2",
    "@babel/preset-env": "^7.1.6",
    "@types/jest": "^26.0.24",
    "babel-jest": "^24.1.0",
    "babel-preset-env": "^1.7.0",
    "eslint": "^5.2.0",
    "eslint-config-developit": "^1.1.1",
    "jest": "^24.1.0",
    "microbundle": "^0.10.1",
    "preact": "^10.2.0",
    "react": "^16.8.3"
  }

I think there is a version mismatch happening but I cannot figure out how to fix it. I tried module resolution but that didn't help. My package.json has these dependencies:

  "dependencies": {
    "htm": "^3.1.0",
    "preact": "^10.5.14",
    "unfetch": "^4.2.0",
    "unistore": "^3.5.2"
  }

The combination which works is:

  "dependencies": {
    "htm": "^3.1.0",
    "preact": "10.2.0",
  }

Can someone please help me figure this out? Version 10.2.0 came more than a year ago and I would like to use the latest version.

@developit
Copy link
Owner

@integryiosama can you show how you're bundling this code? The error you provided happens when there is both a CommonJS and ES Modules version of Preact being used at the same time.

@integryiosama
Copy link
Author

integryiosama commented Jul 28, 2021

@developit thanks for the quick response!

I am using rollup. Here is a simplified version of my config. In dev mode, I use rollup-plugin-livereload and rollup watch mode to rebuild files. In dev mode only the UMD bundle is built, so I can directly use it in an html file to quickly iterate.

import ts from '@wessberg/rollup-plugin-ts';
import { terser } from 'rollup-plugin-terser';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import postcss from 'rollup-plugin-postcss';
import clear from 'rollup-plugin-clear';
import json from '@rollup/plugin-json';

import pkg from './package.json';

const outputDir = './dist';

const getPluginsConfig = (config) => {
  const { prodMode, minify } = config;

  const pluginArr = [
    postcss(),
    commonjs(),
    resolve({
      browser: true,
    }),
    json(),
    ts({
      transpiler: 'babel',
    }),
  ];
  if (minify) {
    pluginArr.push(
      terser({
        output: {
          comments: !prodMode,
        },
      }),
    );
  }
  return pluginArr;
};

export default () => {
  const enableProdMode = !!process.env.PROD_MODE_ENABLED;
  const shoudMinify = !!process.env.MINIFY_ENABLED;
  const shouldEnableHMR = !!process.env.HOT_RELOAD_ENABLED;

  const esmBundle = {
    input: 'src/index.ts',
    output: [
      {
        file: pkg.module,
        format: 'esm',
        sourcemap: true,
      },
    ],
  };

  const umdBundle = {
    input: 'src/index.umd.ts',
    output: [
      {
        file: pkg.main,
        format: 'umd',
        sourcemap: true,
        name: 'SomeName',
      },
    ],
  };

  esmBundle.plugins = [
    clear({
      targets: [`${outputDir}/esm`],
      watch: true,
    }),
    ...getPluginsConfig({
      prodMode: enableProdMode,
      minify: shoudMinify,
      hmr: shouldEnableHMR,
    }),
  ];

  umdBundle.plugins = [
    clear({
      targets: [`${outputDir}/umd`],
      watch: true,
    }),
    ...getPluginsConfig({
      prodMode: enableProdMode,
      minify: shoudMinify,
      hmr: shouldEnableHMR,
    }),
  ];

  const bundlesToBuild = [umdBundle];
  if (!shouldEnableHMR) bundlesToBuild.push(esmBundle);

  return bundlesToBuild;
};

I built a prod UMD bundle and replaced that in my html file, and the error changed to this:

index.js:706 Uncaught (in promise) Error: Hook can only be invoked from render methods.
    at Object.U.__h (index.js:706)
    at we (slicedToArray.js:6)
    at slicedToArray.js:6
    at xe (slicedToArray.js:6)
    at y.Kt [as constructor] (index.ts:38)
    at y.U [as render] (index.ts:30)
    at P (index.ts:30)
    at k (index.ts:30)
    at P (index.ts:30)
    at k (index.ts:30)

@integryiosama
Copy link
Author

integryiosama commented Jul 28, 2021

@integryiosama can you show how you're bundling this code? The error you provided happens when there is both a CommonJS and ES Modules version of Preact being used at the same time.

@developit here's a gist with the generated UMD build if that helps. Really appreciate you helping me out with this!

@marvinhagemeister
Copy link
Collaborator

@integryiosama just had a quick look and the bundle contains two copies of Preact. The concept of hooks in general requires a singleton underneath it, but the different copies of Preact don't know about each other. That's why the hooks don't work.

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

No branches or pull requests

3 participants