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

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

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 测试,要么通过 环境变量 告诉 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.html
匿名

发表评论

匿名网友

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

确定