React Maximum update depth exceeded 错误仅在测试中由useEffect引起
React Maximum update depth exceeded error caused by useEffect only in test
我正在尝试使用 jest 和 react-testing-library 为我的 React 组件编写一些测试。
我的组件看起来像:
//DocumentTable.js
import {useTranslation} from "react-i18next";
import ReactTable from "react-table-6";
import {connect} from "react-redux";
...
export const DocumentTable = ({documents, getDocuments, ...}) => {
const {t} = useTranslation();
const [data, setData] = useState([])
useEffect(() => {
getDocuments();
}, [])
useEffect(() => {
setData(() => translate(documents.map(doc => Object.assign({}, doc))))
}, [documents, t])
const translate = (tempDocuments) => {
if (tempDocuments[0]) {
if (tempDocuments[0].name) {
tempDocuments.forEach(doc => doc.name = t(doc.name));
}
if (tempDocuments[0].documentStatus) {
tempDocuments.forEach(doc => doc.documentStatus = t(doc.documentStatus));
}
}
return tempDocuments;
}
...
return (
<div className="col m-0 p-0 hidden-overflow-y">
<ReactTable
className="bg-dark dark-table"
data={data}
...
)
}
...
export default connect(mapStateToProps, matchDispatchToProps)(DocumentTable);
如你所见,我正在使用 redux 和 react-i18next 的翻译。
我正在使用此组件显示从 react-table-v6 的 ReactTable 组件中的 prop 文档接收到的值。为了避免编辑我的原始值,我创建了文档数组的深层副本,将其转换并放入直接在我的 table.
中使用的数据中
我已经开始编写我的测试,检查我是否可以使用 react-testing-library 正确渲染我的组件:
//DocumentTable.test.js
import React from 'react'
import {render} from '@testing-library/react'
import {DocumentTable} from "../../../components/content/DocumentTable";
import {I18nextProvider} from "react-i18next";
import i18n from "../../../i18n";
it("Should render component", () => {
const documents = [
{
name: "favourite",
documentStatus: "new"
},
{
name: "simple",
documentStatus: "edited"
}
]
render(
<I18nextProvider i18n={i18n}>
<DocumentTable documents={documents} getDocuments={jest.fn()}/>
</I18nextProvider>
);
})
一切似乎都正常。但是,我想像在其他组件测试中那样使用 useTranslation 挂钩的模拟。
我的模拟是:
//_mocks_/react-18next.js
module.exports = {
useTranslation: () => ({
t: key => key,
i18n: {}
}),
}
为了使用它,我已经将 属性 添加到 jest 配置中:
//package.json
"jest": {
"moduleNameMapper": {
"react-i18next": "<rootDir>/src/tests/_mocks_/react-i18next.js"
}
},
并且我已经简化了我的测试:
//DocumentTable.test.js
import React from 'react'
import {render} from '@testing-library/react'
import {DocumentTable} from "../../../components/content/DocumentTable";
it("Should render component", () => {
const documents = [
{
name: "favourite",
documentStatus: "new"
},
{
name: "simple",
documentStatus: "edited"
}
]
render(
<DocumentTable documents={documents} getDocuments={jest.fn()}/>
);
})
现在当我 运行 我的测试时出现以下错误:
Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
in DocumentTable (at DocumentTable.test.js:89)
而且我不明白发生了什么。我得出的结论是,问题是由 DocumentTable.js 文件中的 useEffect 挂钩引起的。当我不创建我的道具的副本而是直接翻译它时:
useEffect(() => {
setData(() => translate(documents))
}, [documents, t])
一切正常。但我必须继续创建它的副本(当用户更改语言时我想再次翻译原始文档)。
我该如何处理?
提前致谢。
问题是您的 mock 每次都会 return 一个新函数 t
,这将触发组件中的 useEffect
,因为 t
是一个依赖项。
使用
//_mocks_/react-18next.js
const t = key => key;
module.exports = {
useTranslation: () => ({
t,
i18n: {}
}),
}
我正在尝试使用 jest 和 react-testing-library 为我的 React 组件编写一些测试。
我的组件看起来像:
//DocumentTable.js
import {useTranslation} from "react-i18next";
import ReactTable from "react-table-6";
import {connect} from "react-redux";
...
export const DocumentTable = ({documents, getDocuments, ...}) => {
const {t} = useTranslation();
const [data, setData] = useState([])
useEffect(() => {
getDocuments();
}, [])
useEffect(() => {
setData(() => translate(documents.map(doc => Object.assign({}, doc))))
}, [documents, t])
const translate = (tempDocuments) => {
if (tempDocuments[0]) {
if (tempDocuments[0].name) {
tempDocuments.forEach(doc => doc.name = t(doc.name));
}
if (tempDocuments[0].documentStatus) {
tempDocuments.forEach(doc => doc.documentStatus = t(doc.documentStatus));
}
}
return tempDocuments;
}
...
return (
<div className="col m-0 p-0 hidden-overflow-y">
<ReactTable
className="bg-dark dark-table"
data={data}
...
)
}
...
export default connect(mapStateToProps, matchDispatchToProps)(DocumentTable);
如你所见,我正在使用 redux 和 react-i18next 的翻译。 我正在使用此组件显示从 react-table-v6 的 ReactTable 组件中的 prop 文档接收到的值。为了避免编辑我的原始值,我创建了文档数组的深层副本,将其转换并放入直接在我的 table.
中使用的数据中我已经开始编写我的测试,检查我是否可以使用 react-testing-library 正确渲染我的组件:
//DocumentTable.test.js
import React from 'react'
import {render} from '@testing-library/react'
import {DocumentTable} from "../../../components/content/DocumentTable";
import {I18nextProvider} from "react-i18next";
import i18n from "../../../i18n";
it("Should render component", () => {
const documents = [
{
name: "favourite",
documentStatus: "new"
},
{
name: "simple",
documentStatus: "edited"
}
]
render(
<I18nextProvider i18n={i18n}>
<DocumentTable documents={documents} getDocuments={jest.fn()}/>
</I18nextProvider>
);
})
一切似乎都正常。但是,我想像在其他组件测试中那样使用 useTranslation 挂钩的模拟。 我的模拟是:
//_mocks_/react-18next.js
module.exports = {
useTranslation: () => ({
t: key => key,
i18n: {}
}),
}
为了使用它,我已经将 属性 添加到 jest 配置中:
//package.json
"jest": {
"moduleNameMapper": {
"react-i18next": "<rootDir>/src/tests/_mocks_/react-i18next.js"
}
},
并且我已经简化了我的测试:
//DocumentTable.test.js
import React from 'react'
import {render} from '@testing-library/react'
import {DocumentTable} from "../../../components/content/DocumentTable";
it("Should render component", () => {
const documents = [
{
name: "favourite",
documentStatus: "new"
},
{
name: "simple",
documentStatus: "edited"
}
]
render(
<DocumentTable documents={documents} getDocuments={jest.fn()}/>
);
})
现在当我 运行 我的测试时出现以下错误:
Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
in DocumentTable (at DocumentTable.test.js:89)
而且我不明白发生了什么。我得出的结论是,问题是由 DocumentTable.js 文件中的 useEffect 挂钩引起的。当我不创建我的道具的副本而是直接翻译它时:
useEffect(() => {
setData(() => translate(documents))
}, [documents, t])
一切正常。但我必须继续创建它的副本(当用户更改语言时我想再次翻译原始文档)。 我该如何处理? 提前致谢。
问题是您的 mock 每次都会 return 一个新函数 t
,这将触发组件中的 useEffect
,因为 t
是一个依赖项。
使用
//_mocks_/react-18next.js
const t = key => key;
module.exports = {
useTranslation: () => ({
t,
i18n: {}
}),
}