Reputation: 696
I'm working on a Next.js app right now using version 12.1.5
, and I'm running into a really crazy bug. Every input component is causing a full re-render on every keystroke, leading to a loss of focus on the input. This applies to input
, textarea
, and even the @monaco-editor/react
package. I had the same issue in Formik
forms, and was weirdly able to solve the problem by switching to react-hook-form
. But, I'm still seeing it on all of my components that accept keystrokes.
I've tried moving the component's value state up the tree, tried putting the state local to the component, and I've had absolutely no luck. This is what my input
component looks like:
Input.tsx
import { forwardRef, InputHTMLAttributes, ReactNode, Ref } from 'react';
import * as LabelPrimitive from '@radix-ui/react-label';
import css from 'classnames';
import { Flex } from 'containers/flex/Flex';
import s from './Input.module.scss';
interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
label?: string;
showLabel?: boolean;
icon?: ReactNode;
}
export const Input = forwardRef(
(
{ showLabel, name, className, label, icon, ...rest }: InputProps,
ref: Ref<HTMLInputElement>
) => {
return (
<Flex column className={css(s.input, className)}>
{showLabel && (
<LabelPrimitive.Label htmlFor={name} className={s.input__label}>
{label}
</LabelPrimitive.Label>
)}
<Flex alignCenter className={s.input__icon}>
{icon && icon}
</Flex>
<input
ref={ref}
name={name}
{...rest}
className={css(s.input__field, icon && s.hasIcon)}
/>
</Flex>
);
}
);
Input.displayName = 'Input';
If I implement a value like this:
TestComponent.tsx
import { useState } from 'react';
import { Input } from 'elements/input/Input';
export const TestComponent = () => {
const [value, setValue] = useState('');
return <Input value={value} onChange={setValue}/>;
};
Any time I try to type I lose focus. Again, this has happened on every implementation of inputs that I've tried outside of react-hook-form
, and includes the @monaco-editor/react
package.
Here, also, is my package.json in case I've got any issues with other packages.
package.json
{
"name": "app",
"version": "0.6.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"test": "jest",
"test:watch": "jest --watch",
"vercel:build": "prisma generate && prisma migrate deploy && next build",
"dev:connect": "pscale connect veroskills development --port 3309",
"shadow:connect": "pscale connect veroskills shadow --port 3309",
"slicemachine": "start-slicemachine",
"prepare": "husky install"
},
"dependencies": {
"@hookform/resolvers": "^2.8.8",
"@monaco-editor/react": "^4.4.4",
"@next-auth/prisma-adapter": "^1.0.3",
"@prisma/client": "^3.13.0",
"@prismicio/client": "^6.4.2",
"@prismicio/helpers": "^2.2.1",
"@prismicio/next": "^0.1.2",
"@prismicio/react": "^2.2.0",
"@prismicio/slice-simulator-react": "^0.2.1",
"@radix-ui/react-alert-dialog": "^0.1.7",
"@radix-ui/react-checkbox": "^0.1.5",
"@radix-ui/react-dialog": "^0.1.7",
"@radix-ui/react-dropdown-menu": "^0.1.6",
"@radix-ui/react-icons": "^1.1.0",
"@radix-ui/react-label": "^0.1.5",
"@radix-ui/react-progress": "^0.1.4",
"@radix-ui/react-scroll-area": "^0.1.4",
"@radix-ui/react-select": "^0.1.1",
"@radix-ui/react-separator": "^0.1.4",
"@radix-ui/react-switch": "^0.1.5",
"@radix-ui/react-tabs": "^0.1.5",
"@radix-ui/react-toast": "^0.1.1",
"@radix-ui/react-toolbar": "^0.1.5",
"@radix-ui/react-tooltip": "^0.1.7",
"bcryptjs": "^2.4.3",
"classnames": "^2.3.1",
"dayjs": "^1.11.0",
"next": "^12.1.5",
"next-auth": "^4.3.1",
"next-compose-plugins": "^2.2.1",
"next-images": "^1.8.4",
"next-react-svg": "^1.1.3",
"node-mocks-http": "^1.11.0",
"path": "^0.12.7",
"plyr-react": "^3.2.1",
"prismic-reactjs": "^1.3.4",
"react": "^18.0.0",
"react-code-blocks": "^0.0.9-0",
"react-dom": "^18.0.0",
"react-gravatar": "^2.6.3",
"react-hook-form": "^7.30.0",
"react-loading-skeleton": "^3.1.0",
"react-select": "^5.2.2",
"react-table": "^7.7.0",
"react-use-keypress": "^1.3.1",
"sass": "^1.50.0",
"swr": "^1.3.0",
"use-monaco": "^0.0.40",
"yup": "^0.32.11"
},
"devDependencies": {
"@prismicio/types": "^0.1.27",
"@types/bcryptjs": "^2.4.2",
"@types/jest": "^27.4.1",
"@types/node": "17.0.21",
"@types/react": "18.0.1",
"@types/react-dom": "^18.0.0",
"@types/react-gravatar": "^2.6.10",
"@types/react-table": "^7.7.10",
"@typescript-eslint/eslint-plugin": "^5.18.0",
"eslint": "8.12.0",
"eslint-config-next": "12.1.4",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-prettier": "^4.0.0",
"husky": "^7.0.0",
"jest": "^27.5.1",
"lint-staged": "^12.3.7",
"prettier": "^2.6.2",
"prisma": "^3.13.0",
"slice-machine-ui": "^0.3.7",
"ts-jest": "^27.1.4",
"turbo": "^1.2.4",
"typescript": "^4.6.3"
},
"lint-staged": {
"**/*.{js,jsx,ts,tsx}": [
"yarn eslint --fix",
"yarn prettier --write"
]
}
}
I am completely at a loss here; does anyone have idea what the issue could be?
Upvotes: 0
Views: 1769
Reputation: 696
I've figured out my problem through some extensive debugging. I have a reusable Flex
component that utilizes createElement
to allow me to create a flex box of any element by passing in an element type- div
, button
, nav
etc, and I use that component within my input component. Getting rid of my createElement
function and replacing it with a simple div
fixed the problem, so now I just need to refactor the Flex
component to still allow for any element type.
All that to say, the Flex
component was causing a full render whenever it's children changed. Off to correct.
Upvotes: 1