Boardy
Boardy

Reputation: 36225

My previously working library returns a weird Cannot read properties of null (reading 'useState') on NextJS 14 Update

I have a NextJS project was running NextJS 12 and I have my own library that I have imported locally for dev work, this library contains some helper functions and re-usable components that I can use in all of my projects.

This works fine in NextJS 12 but have tried upgrading NextJS 13 and then I hit this error where if I include a component from the library, I get the Cannot read properties of null (reading useState)

As a test, I created a brand new NextJS project (although this time it's 14) with a brand new library.

I create a function in the library and use in my new test project and it works perfectly fine.

I add a component to the library and import it into my test project, no errors or warnings but then when I run it I get the below (same error as above in more details)

error using component in test project on NextJS 14

My NextJS project package.json is below:

{
  "name": "new-nextjs-project",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "new-nextjs-library": "file:../new-nextjs-library",
    "next": "13.5.6",
    "react": "^18",
    "react-dom": "^18"
  },
  "devDependencies": {
    "@types/node": "^20",
    "@types/react": "^18",
    "@types/react-dom": "^18",
    "autoprefixer": "^10.0.1",
    "postcss": "^8",
    "tailwindcss": "^3.3.0",
    "typescript": "^5"
  }
}

My tsconfig.json is below:

{
  "compilerOptions": {
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": false,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "paths": {
      "@/*": [
        "./src/*"
      ]
    },
    "forceConsistentCasingInFileNames": true,
    "target": "es5"
  },
  "include": [
    "next-env.d.ts",
    "**/*.ts",
    "**/*.tsx"
  ],
  "exclude": [
    "node_modules"
  ]
}

My test library package.json is below:

{
  "name": "new-nextjs-library",
  "version": "1.0.0",
  "description": "",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "files": [
    "/dist"
  ],
  "script": {
    "build": "tsc"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@types/node": "^20",
    "@types/react": "^18",
    "@types/react-dom": "^18",
    "autoprefixer": "^10.0.1",
    "postcss": "^8",
    "react": "^18",
    "react-dom": "^18",
    "tailwindcss": "^3.3.0",
    "typescript": "^5"
  }
}

My library tsconfig.json is below:

{
  "compilerOptions": {
    "module": "commonjs",
    //"target": "es2015",
    "declaration": true,
    "lib": ["esnext", "dom"],
    "outDir": "./dist",
    "rootDir": "./src",
    "jsx": "react",
    "strict": false,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "target": "ES2018",
    "noEmitOnError": true,
    "downlevelIteration": true,
    "incremental": true
  },
  "include": [
    "src/**/*"
  ]
}

In my src/classes folder I have a file called sayHello which includes the following code which works fine in my main test project

export const sayHello = (firstName: string, lastName: string) => {
    return `${firstName} ${lastName}`
}

My component in my library is below, this is located in /src/components

"use client"

import React, { useState } from 'react';

const Clicker = () => {

    const [clickCount, setClickCount] = useState(0);

    return (
        <>
            <button type='button' onClick={() => setClickCount(prev => prev++)}>
                Click
            </button>
            <p>
                You've clicked {clickCount}
            </p>
        </>
    )
}

export default Clicker

Note I tried adding use client at the top in case it was something to do with server side components in the new NextJS but doesn't make any difference

My library index.ts file is below:

import React from "react";
//Components
import Clicker from "./components/Clicker";

//Classes
import {sayHello} from "./classes/sayHello";

export {
    Clicker,
    sayHello
}

I'm importing the component and using it in my main app as below

import React from "react";
import {NextPage} from "next";
import {Clicker, sayHello} from "new-nextjs-library";


const HomePage : NextPage = () => {

  return (
      <>
        <h1>My New Page</h1>

          <p>Hello {sayHello("Chris", "Board")}</p>
          <Clicker />
      </>
  )
}

export default HomePage;

In the browser console I see the following (added as an image as it didn't format very nicely:

browser console errors

How am I doing an invalid hook, the only hook I'm using is the useState within the functional component is valid.

How can I get this:

cannot read properties of null reading useState.

This makes no sense, and I say this approach has worked on NextJS, but NextJS 13 or 14 I hit this problem.

Upvotes: 2

Views: 323

Answers (1)

Boardy
Boardy

Reputation: 36225

I finally figured the problem.

I had to change my next.config.js file to be called next.config.mjs.

I then had to add the following to this file:

  webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => {
    config.resolve.alias["react"] = path.resolve("./node_modules/react");
    return config;
  }

The complete file looks like the below

import path from "path";

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,

  webpack: (config, { buildId, dev, isServer, defaultLoaders, webpack }) => {
    config.resolve.alias["react"] = path.resolve("./node_modules/react");
    return config;
  }
};

export default nextConfig;

I'm not entirely sure why I have to do this now, npm list react only showed one version of react in the project but as soon as this section was added to the next config and restarted, the library started working again.

Upvotes: 1

Related Questions