错误:在React的生产构建中不支持act(…)。

huangapple go评论75阅读模式
英文:

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

问题

我在我的React Redux应用中有一个特定的情况,我正在从React Testing Library中引入库,并将这些库的导入语句呈现到代码编辑器中作为初始状态:

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;

然而,当我这样做时,在预览窗口中出现运行时错误:

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

我不确定如何解决这个问题,因为RTL不是应用程序本身的依赖项,而是实际被引入的。ESBuild没有找出查找RTL或任何其他库的路径,我正在为ESBuild做这个操作,因为打包器需要它。

我将URL直接提供给ESBuild,以尝试找到特定库的源代码。

在ESBuild插件中是否需要以不同的方式定义 define

let service: esbuild.Service;
const bundle = async (rawCode: string) => {
  if (!service) {
    service = await esbuild.startService({
      worker: true,
      wasmURL: "https://unpkg.com/esbuild-wasm@0.8.27/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;

我尝试通过以下方式安装包:

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

但是在预览窗口中仍然看到运行时错误。

所以问题是,在幕后,当我调用render时,react-testing-library的作者正在用act包装该函数,以使测试尽可能类似于在浏览器中运行的方式运行。这就是我收到此错误的原因。由于您不是在测试环境中运行该代码,因此不支持act

我是否可以编写自己的 render() 函数,并使用它,但只让它看起来像是使用RTL的 render 函数?

英文:

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 &quot;immer&quot;;
import { ActionType } from &quot;../action-types&quot;;
import { Action } from &quot;../actions&quot;;
import { Cell } from &quot;../cell&quot;;
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 &#39;react&#39;;
import { render, screen } from &#39;@testing-library/react&#39;;
import user from &#39;@testing-library/user-event&#39;;
const Counter = () =&gt; {
const [count, setCount] = useState(0);
return &lt;button onClick={() =&gt; setCount((c) =&gt; c + 1)}&gt;Count: {count}&lt;/button&gt;
};
render(&lt;Counter /&gt;);`,
`console.log(a);`,
// Add more initial snippets as needed
];
const reducer = produce((state: CellsState = initialState, action: Action) =&gt; {
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) =&gt; id !== action.payload);
return state;
case ActionType.MOVE_CELL:
const { direction } = action.payload;
const index = state.order.findIndex((id) =&gt; id === action.payload.id);
const targetIndex = direction === &quot;up&quot; ? index - 1 : index + 1;
if (targetIndex &lt; 0 || targetIndex &gt; 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) =&gt; id === action.payload.id
);
if (foundIndex &lt; 0) {
state.order.unshift(cell.id);
} else {
state.order.splice(foundIndex + 1, 0, cell.id);
}
return state;
default:
return state;
}
}, initialState);
const randomId = () =&gt; {
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) =&gt; {
if (!service) {
service = await esbuild.startService({
worker: true,
wasmURL: &quot;https://unpkg.com/esbuild-wasm@0.8.27/esbuild.wasm&quot;,
});
}
try {
const result = await service.build({
entryPoints: [&quot;index.js&quot;],
bundle: true,
write: false,
plugins: [unpkgPathPlugin(), fetchPlugin(rawCode)],
define: {
&quot;process.env.NODE_ENV&quot;: &#39;&quot;production&quot;&#39;,
global: &quot;window&quot;,
},
});
return {
code: result.outputFiles[0].text,
error: &quot;&quot;,
};
} catch (error) {
if (error instanceof Error) {
return {
code: &quot;&quot;,
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?

答案1

得分: 0

只返回翻译好的部分:

要么你正在使用你的应用程序的生产版本来运行你的 jest tests,或者通过一个 环境变量 告诉 jest 将其视为生产环境。

在运行 jest 之前确保设置 NODE_ENV=test,或者一直设置为 NODE_ENV=development

关于一个非常相似的问题,这个 Github 上的问题可能会有帮助 (https://github.com/vercel/next.js/discussions/35676)。


示例 package.json 文件:

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

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:

{  
...
&quot;scripts&quot;: {
&quot;test&quot;: &quot;NODE_ENV=test jest&quot;
},
...
}

huangapple
  • 本文由 发表于 2023年8月9日 09:56:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/76864115-2.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定