I want to test a hook that uses states in its implementation, but each time I run my tests I get this error:
Error: Uncaught [Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.]
The hook is complex and I couldn't find which part may cause an issue (plus it works perfectly fine under real conditions, when running the application with npm start).
I tried to write a dummy test to see if I could figure out anything, and I found that updating a state (which I do in my hook) triggers the error.
Basically, this fails:
import { renderHook } from "#testing-library/react";
import React from "react";
it("foo test", () => {
const { result } = renderHook(() => {
const [foo, setFoo] = React.useState("foo");
setFoo("bar"); // This line is the culprit
return foo;
});
expect(result.current).toEqual("bar");
});
But this works:
import { renderHook } from "#testing-library/react";
import React from "react";
it("foo test", () => {
const { result } = renderHook(() => {
const [foo, setFoo] = React.useState("foo");
return foo;
});
expect(result.current).toEqual("foo");
});
What is the reason for this error and how can I fix it ?
Also, my package.json file:
{
"name": "agora-front",
"version": "0.1.0",
"private": true,
"dependencies": {
"#emotion/react": "^11.10.0",
"#emotion/styled": "^11.10.0",
"#mui/icons-material": "^5.8.4",
"#mui/material": "^5.9.3",
"#testing-library/jest-dom": "^5.16.4",
"#testing-library/react": "^13.3.0",
"#types/react": "^18.0.15",
"#types/react-dom": "^18.0.6",
"i18next": "^21.8.16",
"i18next-browser-languagedetector": "^6.1.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-i18next": "^11.18.3",
"react-router-dom": "^6.3.0",
"react-scripts": "5.0.1",
"typescript": "^4.7.4",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "BROWSER=none react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --verbose",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"#babel/preset-typescript": "^7.18.6",
"#types/jest": "^28.1.6",
"jest": "^28.1.3",
"prettier": "2.7.1",
"react-test-renderer": "^18.2.0"
}
}
I got tricked by the test environment and forgot React basic good practices.
Every state update should be wrapped in a condition with useEffect, otherwise render will trigger endlessly.
I followed that practice in all my React components, thus explaining why it was working fine when building the application. But I wrote my tests a bit quickly.
For my quick example, this is the correct way to do it:
import { renderHook } from "#testing-library/react";
import React from "react";
it("foo test", () => {
const { result } = renderHook(() => {
const [foo, setFoo] = React.useState("foo");
React.useEffect(() => {
setFoo("bar"); // Now it works.
}, [])
return foo;
});
expect(result.current).toEqual("bar");
});
Related
Trying to locally test Dropdown cell editing based on https://glideapps.github.io/glide-data-grid/?path=/story/extra-packages-cells--custom-cell-editing
Grid with cells rendered appropriately.
However, clicking on a cell to choose value from Dropdown, crashes and produces:
The above error occurred in the <Editor> component:
at Editor (http://localhost:3000/static/js/bundle.js:67391:12)
at div
at div
at render (http://localhost:3000/static/js/bundle.js:68194:13)
....
"above error" is
Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for
one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
Code extracted and changed from https://github.com/glideapps/glide-data-grid/blob/8085cd8e0d04c865ab5963af961b734fb053ded9/packages/cells/src/cell.stories.tsx:
import * as React from 'react';
import DataEditor, {
GridCellKind,
DataEditorProps
} from "#glideapps/glide-data-grid";
import { useExtraCells, DropdownCell as DropdownRenderer, DropdownCellType } from "#glideapps/glide-data-grid-cells";
import type { DropdownCell } from "#glideapps/glide-data-grid-cells";
import "#glideapps/glide-data-grid/dist/index.css";
const defaultProps: Partial<DataEditorProps> = {
smoothScrollX: true,
smoothScrollY: true,
isDraggable: false,
rowMarkers: "none",
width: "100%",
};
export const CustomCellEditing: React.VFC = () => {
const cellProps = useExtraCells();
const data = React.useRef<string[]>([]);
return (
<DataEditor
{...defaultProps}
{...cellProps}
onPaste={true}
onCellEdited={(cell, newVal) => {
if (newVal.kind !== GridCellKind.Custom) return;
if (DropdownRenderer.isMatch(newVal)) {
data.current[cell[1]] = newVal.data.value;
}
}}
getCellsForSelection={true}
getCellContent={cell => {
const [, row] = cell;
const val = data.current[row] ?? "A";
return {
kind: GridCellKind.Custom,
allowOverlay: true,
copyData: val,
data: {
kind: "dropdown-cell",
allowedValues: ["A", "B", "C"],
value: val,
},
} as DropdownCellType;
}}
columns={[
{
title: "Dropdown",
width: 200,
},
]}
rows={5}
/>
);
};
export default CustomCellEditing;
index.html body last element:
<div id="portal" />
Config:
{
"name": "xp-ts-mui",
"version": "0.1.0",
"private": true,
"dependencies": {
"#emotion/react": "^11.10.4",
"#emotion/styled": "^11.10.4",
"#glideapps/glide-data-grid": "^5.2.1",
"#glideapps/glide-data-grid-cells": "^5.2.1",
"#mui/icons-material": "^5.10.3",
"#mui/material": "^5.10.3",
"#testing-library/jest-dom": "^5.16.4",
"#testing-library/react": "^13.3.0",
"#testing-library/user-event": "^13.5.0",
"#types/jest": "^27.5.2",
"#types/node": "^16.11.46",
"#types/react": "^18.0.15",
"#types/react-dom": "^18.0.6",
"lodash": "^4.17.21",
"marked": "^4.2.12",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-responsive-carousel": "^3.2.23",
"react-scripts": "5.0.1",
"styled-components": "^5.3.6",
"typescript": "^4.7.4",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
Initial rendering success as:
Crashes when clicking Dropdown cell for selection as:
Extracted and tested another from same https://github.com/glideapps/glide-data-grid/blob/8085cd8e0d04c865ab5963af961b734fb053ded9/packages/cells/src/cell.stories.tsx example based on https://glideapps.github.io/glide-data-grid/?path=/story/extra-packages-cells--custom-cells
All other Custom Cells (e.g. Tag, DatePicker) are working fine with respective editors.
Normal behavior of Tag:
and DatePicker:
Only DropdownCell crashes when clicking for selection.
Any advice please?
I'm using the Header comp from Semantic-ui like this, and I'm having this error for some components, not only Header. All of the components are imported but some of them are not working.
import React, { useEffect, useState } from 'react';
import logo from './logo.svg';
import './App.css';
import axios from 'axios';
import { Header, HeaderContent, List, ListList } from 'semantic-ui-react';
function App() {
const [activities, setActivities]=useState([]);
useEffect(()=>{
axios.get('http://localhost:5000/api/Activites').then(response =>{
console.log(response);
setActivities(response.data);
})
},[])
return (
<div>
<HeaderContent as='h2' icon='users' content='Reactivites' ></HeaderContent>
<ListList>
{activities.map((activity: any)=>(
<List.Item key={activity.id}>
{activity.title}
</List.Item>
))}
</ListList>
</div>
);
}
export default App;
I am using typescript on my react project and semantic-ui for styling
My package.json looks like this, and if its needed to change version of some dependencies give me suggestions on the comments
{
"name": "client-app",
"version": "0.1.0",
"private": true,
"dependencies": {
"#testing-library/jest-dom": "^5.16.4",
"#testing-library/react": "^13.1.1",
"#testing-library/user-event": "^13.5.0",
"#types/jest": "^27.4.1",
"#types/node": "^16.11.31",
"#types/react": "^18.0.8",
"#types/react-dom": "^18.0.0",
"axios": "^0.27.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "5.0.1",
"semantic-ui-css": "^2.4.1",
"semantic-ui-react": "^2.1.2",
"typescript": "^4.6.3",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"postinstall": "semantic-ui-css-patch"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
I know 100% that the error its from the semantic-ui and I would appreciate so much for some help and thank you in forward
I'm new to TypeScript and its my first time using semantic-ui
I have simple functional component like below.
import React, { useState } from 'react';
import { connect } from 'react-redux';
import JoinRoomInputs from '../../../components/JoinRoomPage/JoinRoomInputs/JoinRoomInputs';
import { State } from '../../../store/states/states';
interface Props {
isRoomHost: boolean;
}
const JoinRoomContent = ({ isRoomHost }:Props) => {
const [roomIdValue, setRoomIdValue] = useState('');
const [nameValue, setNameValue] = useState('');
return (
<>
<JoinRoomInputs roomIdValue={roomIdValue} setRoomIdValue={setRoomIdValue} nameValue={nameValue} setNameValue={setNameValue} isRoomHost={isRoomHost} />
</>
);
};
const mapStateToProps = (state:State) => {
return {
...state
}
}
export default connect(mapStateToProps)(JoinRoomContent);
as you can see I have used useState hook in two lines.
But I'm getting yellow warning saying this.
src\components\JoinRoomPage\JoinRoomContent\JoinRoomContent.tsx
Line 1:17: 'useState' is defined but never used #typescript-eslint/no-unused-vars
Search for the keywords to learn more about each warning.
To ignore, add // eslint-disable-next-line to the line before.
And what is more curious is line 17 is this );
This doesn't make any sense to me.
What am I doing wrong here ?
below is my package.json
{
"name": "client",
"version": "0.1.0",
"private": true,
"dependencies": {
"#reduxjs/toolkit": "^1.6.2",
"#testing-library/jest-dom": "^4.2.4",
"#testing-library/react": "^9.5.0",
"#testing-library/user-event": "^7.2.1",
"#types/jest": "^24.9.1",
"#types/node": "^12.20.36",
"#types/react": "^16.14.20",
"#types/react-dom": "^16.9.14",
"#types/react-redux": "^7.1.20",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-redux": "^7.2.6",
"react-router-dom": "^6.0.2",
"react-scripts": "4.0.3",
"typescript": "^4.1.6"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"typescript-plugin-css-modules": "^3.4.0"
}
}
Restart your server or kill the port with the following command:
sudo kill $(sudo lsof -t -i:PORT)
I hope you are having a fantastic day.
I am using this API: https://apify.com/petrpatek/covid-19-aggregator/api, for the first time, and I am having some issues getting it to work with React. This is the file where I am making my API call:
const ApifyClient = require('apify-client');
require('dotenv').config({ path: `${__dirname}/../../.env` });
const client = new ApifyClient({
token: process.env.REACT_APP_API_TOKEN,
});
const input = {};
const getItems = async () => {
// Run the actor and wait for it to finish
const run = await client.actor('petrpatek/covid-19-aggregator').call(input);
// Fetch and print actor results from the run's dataset (if any)
console.log('Results from dataset');
const { items } = await client.dataset(run.defaultDatasetId).listItems();
// console.log(items);
items.forEach((item) => {
console.dir(item);
});
return items;
};
getItems();
// export default getItems();
Most of the above is just copy-pasted from the API docs, this file has no problems running as a standalone, if I run node apify.js, it returns the correct data as I expect:
correct data
However when I try to run it inside of a React component, like so:
import { useEffect } from 'react';
import CountryCard from '../components/countryCard';
import Header from '../components/header';
import getItems from '../APIs/apify';
const AllCountriesPage = () => {
useEffect(() => {
getItems();
}, []);
return (
<div className="allCountriesPage">
<Header />
<CountryCard className="bannerCountry" />
<p className="allCountriesText">All countries</p>
<div className="allCountriesContainer">
{/* Some kind of for loop */}
<CountryCard />
</div>
</div>
);
};
export default AllCountriesPage;
It throws the following error:
error message
At first, I thought this was just a problem with the dependencies, but I do have them installed, or the file would not be running by itself, also this is my package.js file:
{
"name": "catalogue-of-statistics",
"version": "0.1.0",
"private": true,
"dependencies": {
"#testing-library/jest-dom": "^5.11.4",
"#testing-library/react": "^11.1.0",
"#testing-library/user-event": "^12.1.10",
"apify-client": "^1.2.4",
"dotenv": "^8.2.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.3",
"web-vitals": "^1.0.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"#babel/core": "^7.14.5",
"#babel/eslint-parser": "^7.14.5",
"#babel/plugin-syntax-jsx": "^7.14.5",
"#babel/preset-react": "^7.14.5",
"eslint": "^7.28.0",
"eslint-config-airbnb": "^18.2.1",
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-react": "^7.24.0",
"eslint-plugin-react-hooks": "^4.2.0",
"stylelint": "^13.13.1",
"stylelint-config-standard": "^21.0.0",
"stylelint-csstree-validator": "^1.9.0",
"stylelint-scss": "^3.19.0"
}
}
I see my colleague replied to you over our Discord.
For others:
You need to add mainFields: ['browser', 'main', 'module'], to your webpack config. The order is important. main needs to go before module. See https://webpack.js.org/configuration/resolve/#resolvemainfields
As Lukas was saying above, the Apify team answered my question directly in their discord, so I want to put the solution here if someone comes across it in the future.
You need to go to node_modules -> react_scripts -> config -> webpack.config.js inside the resolve: { ... } object, add this line: mainFields: ['browser', 'main', 'module'], make sure that main is before module.
I created a React app with create-react last week.
I have a simple form that displays a message when I click submit.
I would like to test it, this is the test i created SampleForm.test.tsx:
import React from "react";
import { render, fireEvent, screen, waitFor } from "#testing-library/react";
import SampleForm from "./SampleForm";
import "#testing-library/jest-dom/extend-expect"
test("renders submits form", async () => {
const str = "THIS DIDNT DO ANYTHING";
const { container, getByText, findByText } = render(<SampleForm />);
fireEvent.click(screen.getByText("Submit"));
await waitFor(() => expect(screen.getByText(str)))
});
I'm getting an error at waitFor
TypeError: MutationObserver is not a constructor
stack trace:
at node_modules/#testing-library/dom/dist/wait-for.js:38:22
at waitFor (node_modules/#testing-library/dom/dist/wait-for.js:31:10)
at node_modules/#testing-library/dom/dist/wait-for.js:70:54
at node_modules/#testing-library/react/dist/pure.js:51:22
at node_modules/#testing-library/react/dist/act-compat.js:60:24
at batchedUpdates$1 (node_modules/react-dom/cjs/react-dom.development.js:21856:12)
at act (node_modules/react-dom/cjs/react-dom-test-utils.development.js:929:14)
at node_modules/#testing-library/react/dist/act-compat.js:59:20
at asyncAct (node_modules/#testing-library/react/dist/act-compat.js:38:14)
at Object.asyncWrapper (node_modules/#testing-library/react/dist/pure.js:50:35)
at waitForWrapper (node_modules/#testing-library/dom/dist/wait-for.js:70:35)
at Object.<anonymous> (src/components/SampleForm.test.tsx:18:11)
I have tried several different alterations of the style and text querying. Derived off of the samples here
This is the straight example i'm trying to get to work
I'm hesitant about adding shims and gyrations because according to the example I shouldn't have to do any of that. I'd like to understand why that example isn't working.
My package.json:
{
"name": "intact",
"version": "0.1.0",
"private": true,
"dependencies": {
"#rjsf/core": "^2.0.0-alpha.6",
"#testing-library/jest-dom": "^5.3.0",
"#testing-library/react": "^10.0.2",
"#testing-library/user-event": "^10.0.1",
"#types/jest": "^25.1.5",
"#types/lodash": "^4.14.149",
"#types/react": "^16.9.0",
"#types/react-dom": "^16.9.0",
"#types/react-router-dom": "^5.1.3",
"bootstrap": "^4.4.1",
"lodash": "^4.17.15",
"msal": "^1.2.2",
"react": "^16.13.1",
"react-aad-msal": "^2.3.4",
"react-bootstrap": "^1.0.0",
"react-dom": "^16.13.1",
"react-router-dom": "^5.1.2",
"react-scripts": "3.4.1",
"typescript": "^3.8.3"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"#types/fs-extra": "^8.1.0",
"#types/node": "^13.11.0",
"#types/rimraf": "^3.0.0",
"fs-extra": "^9.0.0",
"rimraf": "^3.0.2",
"ts-node": "^8.8.1"
}
}
edits: 04-05-2020
Are you running latest CRA? If so then this issue https://github.com/facebook/create-react-app/pull/8362 might be what you're experiencing. That's solvable by installing https://www.npmjs.com/package/jest-environment-jsdom-sixteen, e.g. $ yarn add -D jest-environment-jsdom-sixteen and editing your test script:
...
"scripts": {
...
- "test": "react-scripts test --env=dom"
+ "test": "react-scripts test --env=jest-environment-jsdom-sixteen"
...
},
...
"devDependencies": {
...
"jest-environment-jsdom-sixteen": "^1.0.3",
...
},
...
Essentially you will tell your jest to use jsdom v16 instead of v15(default for Jest v25).
I also faced the same issue.
Before trying the accepted answer, I updated react-scripts to version 4.0.1.
That solved the issue, because react-scripts 4.0.1 uses Jest 26.6.x.
Therefore, try upgrading react-scripts first.
I had this problem when I was running a very old version of node.js on Windows. When I updated to node version 12.16.3, the error disappeared.
This is my scene:
jest.config.js:
module.exports = {
preset: 'ts-jest/presets/js-with-ts',
testEnvironment: 'enzyme',
setupFilesAfterEnv: ['jest-enzyme'],
setupFiles: ['./jest.setup.js'],
testEnvironmentOptions: {
enzymeAdapter: 'react16',
},
};
package.json:
"#testing-library/jest-dom": "^5.11.6",
"#testing-library/react": "^11.2.2",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.5",
"jest": "^26.6.3",
"jest-environment-enzyme": "^7.1.2",
"jest-enzyme": "^7.1.2",
"ts-jest": "^26.4.4",
"typescript": "^4.1.2"
"react": "^16.14.0",
"react-dom": "^16.14.0",
"react-router-dom": "^5.2.0",
Got error:
TypeError: MutationObserver is not a constructor
9 | const { getByTestId } = render(<Login />);
10 | expect(getByTestId('getUrl').getAttribute('href')).toEqual('');
> 11 | const url = await waitFor(() => getByTestId('getUrl'));
But when I changed the testEnvironment configuration:
module.exports = {
preset: 'ts-jest/presets/js-with-ts',
// testEnvironment: 'enzyme',
setupFilesAfterEnv: ['jest-enzyme'],
setupFiles: ['./jest.setup.js'],
testEnvironmentOptions: {
enzymeAdapter: 'react16',
},
};
The test passes:
PASS examples/65336283/LoginLink.test.tsx
65336283
✓ should display loginurl (23 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 3 s, estimated 4 s
In my case, I was updating from an old version of jest and had to make sure to include the appropriate testing environment in package.json as none was set:
{
...
"scripts": {
...
"test": "jest --env=jsdom"
...
},
...
}