form
form copied to clipboard
Every input field in form rerenders when just one changed
Ideally, only one input field component should render when its value is changed. Currently, all other input in the same form rerender, sometimes like 4 times each (when it comes to validation). Any suggestions?
import React, { useState } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
import { useForm, useField, splitFormProps } from "react-form";
async function sendToFakeServer(values) {
await new Promise(resolve => setTimeout(resolve, 1000));
return values;
}
function validateAddressStreet(value) {
if (!value) {
return "A street is required";
}
return false;
}
async function fakeCheckValidName(name, instance) {
if (!name) {
return "A name is required";
}
return instance.debounce(async () => {
console.log("checking name");
await new Promise(resolve => setTimeout(resolve, 1000));
// All names are valid, so return a false error
return false;
}, 500);
}
let counter = 0;
const InputField = React.forwardRef((props, ref) => {
const [id] = useState(() => counter++);
console.log(`InputField#${id}.render()`);
// Let's use splitFormProps to get form-specific props
const [field, fieldOptions, rest] = splitFormProps(props);
// Use the useField hook with a field and field options
// to access field state
const {
meta: { error, isTouched, isValidating },
getInputProps
} = useField(field, fieldOptions);
// Build the field
return (
<>
<input {...getInputProps({ ref, ...rest })} />{" "}
{isValidating ? (
<em>Validating...</em>
) : isTouched && error ? (
<em>{error}</em>
) : null}
</>
);
});
function MyForm() {
// Use the useForm hook to create a form instance
const {
Form,
meta: { isSubmitting, canSubmit }
} = useForm({
onSubmit: async (values, instance) => {
// onSubmit (and everything else in React Form)
// has async support out-of-the-box
await sendToFakeServer(values);
console.log("Huzzah!");
},
debugForm: true
});
return (
<Form>
<div>
<label>
Name: <InputField field="name" validate={fakeCheckValidName} />
</label>
</div>
<div>
<label>
Address Street:{" "}
<InputField field="address.street" validate={validateAddressStreet} />
</label>
</div>
<div>
<button type="submit" disabled={!canSubmit}>
Submit
</button>
</div>
<div>
<em>{isSubmitting ? "Submitting..." : null}</em>
</div>
</Form>
);
}
function App() {
return <MyForm />;
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
PS: This seems to be a classic unsolved problem for every react forms library: https://github.com/jaredpalmer/formik/issues/342 https://github.com/redux-form/redux-form/issues/1405
Are they slow renders? Renders are not bad, but slow renders are. Profile if you can and post your findings. 👍 On Feb 3, 2020, 2:03 PM -0700, Andrii Polishchuk [email protected], wrote:
Ideally, only one input field component should render on it's value change. Currently, all other input in the same form rerender, sometimes like 4 times each (when it comes to validation). Any suggestions? import React, { useState } from "react"; import ReactDOM from "react-dom";
import "./styles.css";
import { useForm, useField, splitFormProps } from "react-form";
async function sendToFakeServer(values) { await new Promise(resolve => setTimeout(resolve, 1000)); return values; }
function validateAddressStreet(value) { if (!value) { return "A street is required"; } return false; }
async function fakeCheckValidName(name, instance) { if (!name) { return "A name is required"; }
return instance.debounce(async () => { console.log("checking name"); await new Promise(resolve => setTimeout(resolve, 1000)); // All names are valid, so return a false error return false; }, 500); }
let counter = 0; const InputField = React.forwardRef((props, ref) => { const [id] = useState(() => counter++); console.log(
InputField#${id}.render()
); // Let's use splitFormProps to get form-specific props const [field, fieldOptions, rest] = splitFormProps(props);// Use the useField hook with a field and field options // to access field state const { meta: { error, isTouched, isValidating }, getInputProps } = useField(field, fieldOptions);
// Build the field return ( <> <input {...getInputProps({ ref, ...rest })} />{" "} {isValidating ? ( Validating... ) : isTouched && error ? ( {error} ) : null} </> ); });
function MyForm() { // Use the useForm hook to create a form instance const { Form, meta: { isSubmitting, canSubmit } } = useForm({ onSubmit: async (values, instance) => { // onSubmit (and everything else in React Form) // has async support out-of-the-box await sendToFakeServer(values); console.log("Huzzah!"); }, debugForm: true });
return (
); }function App() { return <MyForm />; }
const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement); — You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub, or unsubscribe.
@tannerlinsley Renders are slow or not depending on the implementation of components and the number of components in form. You may be interested to check the landing page of this alternative library, it describes the general idea how to fix this issue: https://react-hook-form.com/
Do you ever wonder how many component re-renders have been triggered by the user? React Hook Form embraces uncontrolled form validation to reduce unnecessary performance impact.
@apo91 yes, but for which price? they use MutationObservers under the hood.
This one has zero extra rerenders while being a controlled kind of form. I tested this. It uses event emitter. It is fast. https://github.com/JoviDeCroock/Hooked-Form
Just to bump this, I'm getting 6 rerenders on a simple form with two fields. This causes an input delay of around 40ms, which sounds small but feels very noticeable, especially when typing fast.
https://github.com/jaredpalmer/formik/issues/342#issuecomment-646155810
I have the same problem
@NairiAreg what version are you running into this with?