-
-
Notifications
You must be signed in to change notification settings - Fork 7k
Open
Labels
bugSomething isn't workingSomething isn't working
Description
Describe the bug
The react-hooks/purity ESLint rule based on the React Compiler reports Cannot call impure function during render with the Math.random() call in the SidebarMenuSkeleton component:
function SidebarMenuSkeleton({
className,
showIcon = false,
...props
}: React.ComponentProps<"div"> & {
showIcon?: boolean
}) {
// Random width between 50 to 90%.
const width = React.useMemo(() => {
return `${Math.floor(Math.random() * 40) + 50}%` // 💥 react-hooks/purity error, see details below
}, [])Affected component/components
Sidebar
How to reproduce
- Create a Next.js app
$ mkdir repro-shadcnui-react-hooks-purity-error $ cd repro-shadcnui-react-hooks-purity-error $ pnpm create [email protected] . --app --no-turbopack --no-src-dir --no-eslint --import-alias @/\* --tailwind --typescript Creating a new Next.js app in /Users/k/p/repro-shadcnui-react-hooks-purity-error. ... dependencies: + next 15.5.5 + react 19.1.0 + react-dom 19.1.0 devDependencies: + @tailwindcss/postcss 4.1.14 + @types/node 20.19.21 + @types/react 19.2.2 + @types/react-dom 19.2.2 + tailwindcss 4.1.14 + typescript 5.9.3
- Use shadcn/ui to add a sidebar component
$ pnpm dlx shadcn@latest add sidebar ... ✔ Created 8 files: ... - components/ui/sidebar.tsx
- Install ESLint dependencies
$ pnpm add --save-dev eslint @eslint/js typescript typescript-eslint eslint-config-flat-gitignore eslint-plugin-react-hooks Packages: +157 -2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- Progress: resolved 295, reused 246, downloaded 2, added 37, done devDependencies: + @eslint/js 9.37.0 (9.38.0 is available) + eslint 9.37.0 (9.38.0 is available) + eslint-config-flat-gitignore 2.1.0 + eslint-plugin-react-hooks 7.0.0 + typescript 5.9.3 + typescript-eslint 8.46.1 (8.46.2 is available) Done in 3s using pnpm v10.19.0
- Configure the
react-hooks/purityrule ineslint.config.mjs, based partly on thetypescript-eslintGetting Started docsimport eslint from '@eslint/js'; import gitignore from 'eslint-config-flat-gitignore'; import reactHooks from 'eslint-plugin-react-hooks'; import tseslint from 'typescript-eslint'; export default tseslint.config( gitignore(), eslint.configs.recommended, tseslint.configs.recommended, { ignores: ['.next/**'], plugins: { 'react-hooks': reactHooks, }, rules: { 'react-hooks/purity': 'error', }, }, );
- Lint the Sidebar file and receive an error 💥
$ pnpm eslint components/ui/sidebar.tsx --max-warnings 0 /Users/k/p/repro-shadcnui-react-hooks-purity-error/components/ui/sidebar.tsx 611:26 error Error: Cannot call impure function during render `Math.random` is an impure function. Calling an impure function can produce unstable results that update unpredictably when the component happens to re-render. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent). /Users/k/p/repro-shadcnui-react-hooks-purity-error/components/ui/sidebar.tsx:611:26 609 | // Random width between 50 to 90%. 610 | const width = React.useMemo(() => { > 611 | return `${Math.floor(Math.random() * 40) + 50}%` | ^^^^^^^^^^^^^ Cannot call impure function 612 | }, []) 613 | 614 | return ( react-hooks/purity ✖ 1 problem (1 error, 0 warnings)
Codesandbox/StackBlitz link
- https://codesandbox.io/p/devbox/sweet-wildflower-mtmqdw?file=%2Fcomponents%2Fui%2Fsidebar.tsx%3A611%2C53&workspaceId=ws_GfAuHrswXyA1DoeSwsjjjz
- GitHub reproduction: https://github.com/karlhorky/repro-shadcnui-react-hooks-purity-error
Suggested solution
Switch useMemo to useState as per the example in the react-hooks/purity docs, disabling the react-naming-convention/use-state rule if necessary:
- const width = React.useMemo(() => {
+ // eslint-disable-next-line react-naming-convention/use-state -- Allow value-only state destructuring for resolving react-hooks/purity problem https://github.com/shadcn-ui/ui/issues/8540
+ const [width] = React.useState(() => {
return `${Math.floor(Math.random() * 40) + 50}%`
- }, [])
+ })Logs
System Info
eslint-plugin-react-hooks 7.0.0Before submitting
- I've made research efforts and searched the documentation
- I've searched for existing issues
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't working