Search code examples
reactjsreact-reduxesbuild

Error: act(...) is not supported in production builds of React


I have a specific case here where in my React Redux app I am pulling in libraries from React Testing Library and rendering the import statements of those libraries into a code editor as initial state:

import produce from "immer";
import { ActionType } from "../action-types";
import { Action } from "../actions";
import { Cell } from "../cell";

interface CellsState {
  loading: boolean;
  error: string | null;
  order: string[];
  data: {
    [key: string]: Cell;
  };
}

const initialState: CellsState = {
  loading: false,
  error: null,
  order: [],
  data: {},
};

const initialCodeSnippets = [
  `import React, { useState } from 'react';
  import { render, screen } from '@testing-library/react';
  import user from '@testing-library/user-event';
  
  const Counter = () => {
    const [count, setCount] = useState(0);
    return <button onClick={() => setCount((c) => c + 1)}>Count: {count}</button>
  };
  render(<Counter />);`,
  `console.log(a);`,
  // Add more initial snippets as needed
];

const reducer = produce((state: CellsState = initialState, action: Action) => {
  switch (action.type) {
    case ActionType.UPDATE_CELL:
      const { id, content } = action.payload;

      state.data[id].content = content;
      return state;
    case ActionType.DELETE_CELL:
      delete state.data[action.payload];
      state.order = state.order.filter((id) => id !== action.payload);

      return state;
    case ActionType.MOVE_CELL:
      const { direction } = action.payload;
      const index = state.order.findIndex((id) => id === action.payload.id);
      const targetIndex = direction === "up" ? index - 1 : index + 1;

      if (targetIndex < 0 || targetIndex > state.order.length - 1) {
        return state;
      }

      state.order[index] = state.order[targetIndex];
      state.order[targetIndex] = action.payload.id;

      return state;
    case ActionType.INSERT_CELL_AFTER:
      const cell: Cell = {
        content: initialCodeSnippets[action.payload.orderIndex],
        type: action.payload.type,
        id: randomId(),
        orderIndex: action.payload.orderIndex,
      };

      state.data[cell.id] = cell;

      const foundIndex = state.order.findIndex(
        (id) => id === action.payload.id
      );

      if (foundIndex < 0) {
        state.order.unshift(cell.id);
      } else {
        state.order.splice(foundIndex + 1, 0, cell.id);
      }

      return state;
    default:
      return state;
  }
}, initialState);

const randomId = () => {
  return Math.random().toString(36).substring(2, 5);
};

export default reducer;

However, when I do so, in the preview window I get a runtime error saying that:

Error: act(...) is not supported in production builds of React

I am not sure how to resolve this, since RTL is not a dependency in the application itself but actually being pulled in. ESBuild is not figuring out what path to look at to find RTL or any other library, I am doing it for ESBuild because the bundler needs it.

I take the URL and provide it directly to ESBuild to try to find the source code for a particular library.

Do I need to define define differently in the ESBuild plugin?

let service: esbuild.Service;
const bundle = async (rawCode: string) => {
  if (!service) {
    service = await esbuild.startService({
      worker: true,
      wasmURL: "https://unpkg.com/[email protected]/esbuild.wasm",
    });
  }

  try {
    const result = await service.build({
      entryPoints: ["index.js"],
      bundle: true,
      write: false,
      plugins: [unpkgPathPlugin(), fetchPlugin(rawCode)],
      define: {
        "process.env.NODE_ENV": '"production"',
        global: "window",
      },
    });

    return {
      code: result.outputFiles[0].text,
      error: "",
    };
  } catch (error) {
    if (error instanceof Error) {
      return {
        code: "",
        error: error.message,
      };
    } else {
      throw error;
    }
  }
};

export default bundle;

I tried installing the packages via:

npm install @testing-library/jest-dom @testing-library/react @testing-library/user-event --force

But I still see that runtime error in the preview window.

So the problem is that under the hood, when I'm calling render, the authors of react-testing-library are wrapping that function in act to make the test run as similar to how it runs in the browser as it can. That's why I am getting this error. Since you're not running that code inside a test environment, act is not supported.

Could I maybe write my own render() function and using it instead, but only making it seem like it's using the render function from RTL?


Solution

  • You are either running your jest tests with the production build of your application or telling jest to treat it as if it were production with an environment variable.

    Make sure to set NODE_ENV=test before running jest or set NODE_ENV=development all the way. This issue on Github about a very similar problem might be helpful (https://github.com/vercel/next.js/discussions/35676).


    sample package.json file:

    {  
       ...
       "scripts": {
         "test": "NODE_ENV=test jest"
       },
       ...
    }