In the code shown here, when I am trying to enter character in each input field, it constantly is re-rendering the whole component as I checked in react dev tools by highlight component while state update.
Like: username input: when I pressed "abc", it re-rendered the component 3 times. That is very bad for performance.
On each input field, I am facing this issue - how to prevent that?Can you provide a solution that I can dive deeper into that? Thanks.
import React, { useState } from 'react';
import {
CButton,
CCard,
CCardBody,
CCol,
CContainer,
CForm,
CFormInput,
CInputGroup,
CInputGroupText,
CRow,
} from '@coreui/react';
import CIcon from '@coreui/icons-react';
import axios from 'axios';
import InputMask from 'react-input-mask';
import { cilLocationPin, cilLockLocked, cilUser, cilPhone } from '@coreui/icons';
import { useNavigate } from 'react-router-dom';
const UserRegister = () => {
console.log('customer register');
const navigate = useNavigate();
const [validated, setValidate] = useState(false);
const [inputValue, setInputValue] = useState({
userName: '',
email: '',
password: '',
repeatPassword: '',
location: '',
phoneNumber: '',
});
const [error, setError] = useState({
userName: '',
email: '',
password: '',
repeatPassword: '',
location: '',
phoneNumber: '',
});
const handleInputBlur = (e) => {
const { name, value } = e.currentTarget;
if (name === 'userName' && (value.length > 20 || value.length < 5)) {
setError((prevValues) => ({
...prevValues,
[name]: 'input length between 5 to 20',
}));
} else if (name === 'location' && (value.length > 30 || value.length < 3)) {
setError((prevValues) => ({
...prevValues,
[name]: 'input length between 3 to 30',
}));
} else if (name === 'phoneNumber' && value.includes('_')) {
setError((prevValues) => ({
...prevValues,
[name]: 'input length must be 10',
}));
} else if (
name === 'email' &&
!/^([a-zA-Z0-9._%-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})$/i.test(value)
) {
setError((prevValues) => ({
...prevValues,
[name]: 'enter valid email address',
}));
} else if (name === 'password' && value.length < 8) {
setError((prevValues) => ({
...prevValues,
[name]: 'password length is very less',
}));
} else if (
name === 'repeatPassword' &&
!(
value.length === inputValue.password.length &&
value === document.getElementsByName('password')[0].value
)
) {
setError((prevValues) => ({
...prevValues,
[name]: 'password & repeat password must be same',
}));
}
};
const handleInputChange = (e) => {
const { name, value } = e.target;
setInputValue((prevValues) => ({
...prevValues,
[name]: value,
}));
if (name === 'phoneNumber' && value.includes('_')) {
setError((prevValues) => ({
...prevValues,
[name]: 'phone number length must be 10',
}));
} else {
setError((prevValues) => ({
...prevValues,
[name]: null,
}));
}
};
const handleSubmit = async (e) => {
if (
inputValue.userName === '' ||
inputValue.email === '' ||
inputValue.password === '' ||
inputValue.repeatPassword === '' ||
inputValue.location === '' ||
inputValue.phoneNumber === ''
) {
setValidate(true);
e.preventDefault();
e.stopPropagation();
} else {
try {
setValidate(true);
const url = 'http://localhost:3000/api/booking/customerRegistration';
const response = await axios.post(url, inputValue);
// console.log('form values', inputValue);
if (response.status === 200) {
window.alert('registration successful');
console.log('inputValue', inputValue);
navigate('/login');
} else {
window.alert('Email Already Exist');
console.log(response.data);
}
} catch (err) {
console.log('err', err);
}
}
};
return (
<div className="bg-light min-vh-100 d-flex flex-row align-items-center">
<CContainer>
<CRow className="justify-content-center">
<CCol md={9} lg={7} xl={6}>
<CCard className="mx-4">
<CCardBody className="p-4">
<CForm validated={validated}>
<h1>Register</h1>
<p className="text-medium-emphasis">Create your account</p>
<CInputGroup className="mb-3">
<CInputGroupText>
<CIcon icon={cilUser} />
</CInputGroupText>
<CFormInput
placeholder="Username"
name="userName"
value={inputValue.userName}
onChange={handleInputChange}
onBlur={handleInputBlur}
required
minLength={5}
maxLength={20}
autoComplete="username"
feedbackInvalid="your name is required"
/>
</CInputGroup>
{error && error?.userName ? (
<div style={{ fontSize: '14px', color: '#e65a5a' }}>{error?.userName}</div>
) : (
''
)}
<CInputGroup className="mb-3">
<CInputGroupText>@</CInputGroupText>
<CFormInput
placeholder="Email"
type="email"
name="email"
value={inputValue.email}
onChange={handleInputChange}
onBlur={handleInputBlur}
required
autoComplete="email"
feedbackInvalid="email is required"
/>
</CInputGroup>
{error && error?.email ? (
<div style={{ fontSize: '14px', color: '#e65a5a' }}>{error?.email}</div>
) : (
''
)}
<CInputGroup className="mb-3">
<CInputGroupText>
<CIcon icon={cilLockLocked} />
</CInputGroupText>
<CFormInput
type="password"
name="password"
placeholder="Password"
autoComplete="new-password"
value={inputValue.password}
minLength={8}
onChange={handleInputChange}
onBlur={handleInputBlur}
feedbackInvalid="password is required"
required
/>
</CInputGroup>
{error && error?.password ? (
<div style={{ fontSize: '14px', color: '#e65a5a' }}>{error?.password}</div>
) : (
''
)}
<CInputGroup className="mb-4">
<CInputGroupText>
<CIcon icon={cilLockLocked} />
</CInputGroupText>
<CFormInput
type="password"
name="repeatPassword"
placeholder="Repeat password"
autoComplete="new-password"
value={inputValue.repeatPassword}
minLength={8}
maxLength={inputValue.password.length}
onChange={handleInputChange}
onBlur={handleInputBlur}
feedbackInvalid="repeat password is required"
required
/>
</CInputGroup>
{error && error?.repeatPassword ? (
<div style={{ fontSize: '14px', color: '#e65a5a' }}>
{error?.repeatPassword}
</div>
) : (
''
)}
<CInputGroup className="mb-3">
<CInputGroupText>
<CIcon icon={cilLocationPin} />
</CInputGroupText>
<CFormInput
placeholder="Location"
name="location"
value={inputValue.location}
onChange={handleInputChange}
onBlur={handleInputBlur}
minLength={3}
maxLength={30}
feedbackInvalid="location is required"
required
autoComplete="location"
/>
</CInputGroup>
{error && error?.location ? (
<div style={{ fontSize: '14px', color: '#e65a5a' }}>{error?.location}</div>
) : (
''
)}
<CInputGroup className="mb-3">
<CInputGroupText>
<CIcon icon={cilPhone} />
</CInputGroupText>
<InputMask
mask="99999-99999"
maskChar="_"
value={inputValue.phoneNumber}
onChange={handleInputChange}
onBlur={handleInputBlur}
required
>
{(inputProps) => (
<CFormInput
type="text"
id="phoneNumber"
name="phoneNumber"
placeholder="Enter Phone Number"
{...inputProps}
feedbackInvalid={'phone number is required'}
/>
)}
</InputMask>
</CInputGroup>
{error && error?.phoneNumber ? (
<div style={{ fontSize: '14px', color: '#e65a5a' }}>{error?.phoneNumber}</div>
) : (
''
)}
<div className="d-grid">
<CButton color="success" type="button" onClick={handleSubmit}>
Create Account
</CButton>
</div>
</CForm>
</CCardBody>
</CCard>
</CCol>
</CRow>
</CContainer>
</div>
);
};
export default UserRegister;
A few options that you can look into:
Put the input component into a child component, and put all the logic for input inside of that component, this way only the child component will rerender
useRefinstead ofuseStateChange in
refwill not cause rerendershttps://codedamn.com/news/reactjs/a-comprehensive-guide-to-using-useref-hook
Debouncing is removing unwanted input noise from buttons, switches or other user input. Debouncing prevents extra activations or slow functions from triggering too often.
https://www.geeksforgeeks.org/lodash-_-debounce-method/