rushang panchal
rushang panchal

Reputation: 49

What is best way to create forms in react?

I am beginner in react. I have following code:

import React, { useState, useEffect } from 'react';
import { Card, Form, Button } from 'react-bootstrap';
import Axios from 'axios'

export function StudentForm({ student, onSuccess, onError, setState }) {
    const url = `http://localhost:9899/api/StudentData`;

    const intialStudent = { Firstname: '', Middlename: '', Lastname: '', DOB: '', Gender: '' };

    const [Student, setStudent] = useState(intialStudent);

    useEffect(() => {
        setStudent(student ? student : intialStudent);
    }, [student]);

    const SaveData = function (studentData) {

        if (student._id) {
            Axios.post(url, { ...studentData }, { headers: { 'accept': 'application/json' } })
                .then(res => {
                    setState(null);
                    onSuccess(res);

                })
                .catch(error => {
                    alert('Error To Edit data');
                });
        }
        else {
            Axios.post(url, studentData, { headers: { 'accept': 'application/json' } })
                .then(res => {
                    setState(null);
                    onSuccess(res);
                })
                .catch(err => onError(err));
        }
    }
    return (
        <Card>
            <Card.Header><h5>{student ? "Edit" : "Add"} Student</h5></Card.Header>
            <Card.Body>
                <Form onSubmit={(e) => { e.preventDefault(); SaveData(Student); }}>
                    <Form.Group><Form.Control type="text" name="Firstname" placeholder="Firstname" value={Student.Firstname} onChange={e => { setStudent({ ...Student, Firstname: e.target.value }) }} /></Form.Group>
                    <Form.Group><Form.Control type="text" name="Middlename" placeholder="Middlename" value={Student.Middlename} onChange={e => setStudent({ ...Student, Middlename: e.target.value })} /></Form.Group>
                    <Form.Group><Form.Control type="text" name="Lastname" placeholder="Lastname" value={Student.Lastname} onChange={e => setStudent({ ...Student, Lastname: e.target.value })} /></Form.Group>
                    <Form.Group><Form.Control type="date" name="DOB" placeholder="DOB" value={Student.DOB} onChange={e => setStudent({ ...Student, DOB: e.target.value })} /></Form.Group>
                    <Form.Group><Form.Control type="text" name="Gender" placeholder="Class" value={Student.Gender} onChange={e => setStudent({ ...Student, Gender: e.target.value })} /></Form.Group>
                    <Button variant="primary" type="submit">Submit</Button>
                </Form>
            </Card.Body>
        </Card>
    );
}

In above code I am setting state on change event on each field. So it will render again and again when I change any of the field.If it is large form so it may take a lot of time to re-render so is there a better way to create to handle this kind of situation, or any best practices for using forms with react?

Upvotes: 3

Views: 2118

Answers (3)

mehmetdemiray
mehmetdemiray

Reputation: 1116

You can use only one Function for all onChanges. Looks like this;

<Form.Group>
  <Form.Control
     type="text"
     name="Firstname"
     placeholder="Firstname"
     value={Student.Firstname}
     onChange={handleChange} 
  />
</Form.Group>

And this is your handleChange function;

const handleChange = e => {
  const {name, value} = e.target
  setValues({...values, [name]: value})
}

This is your state;

const [values, setValues] = useState({
  Firstname: "", 
  Middlename: "", 
  Lastname: "",
  DOB: "",
  Gender: ""
})

I think this way is more effective with less code.

Upvotes: 2

HMR
HMR

Reputation: 39250

You have to re render the form when an input changed but you don't need to re render every input when you make sure the onChange function doesn't change reference on every render and your input is a pure component (using React.memo for functional component and inherit from React.PureComponent for class components).

Here is an example of optimized inputs.

const {
  useEffect,
  useCallback,
  useState,
  memo,
  useRef,
} = React;
function App() {
  return <StudentForm />;
}
//put initial student here so it doesn't change reference and quits the linter
//  in useEffect
const initialStudent = {
  Firstname: '',
  Middlename: '',
};
function StudentForm({ student }) {
  const [Student, setStudent] = useState(initialStudent);
  //useCallback so onChange is not re created and causes re rendering
  //  of components that didn't change
  const onChange = useCallback(
    (key, value) =>
      setStudent(student => ({ ...student, [key]: value })),
    []
  );

  useEffect(() => {
    setStudent(student ? student : initialStudent);
  }, [student]);

  const SaveData = function(studentData) {
    console.log('saving data:', studentData);
  };
  return (
    <form
      onSubmit={e => {
        e.preventDefault();
        SaveData(Student);
      }}
    >
      <InputContainer
        type="text"
        name="Firstname"
        placeholder="Firstname"
        value={Student.Firstname}
        stateKey="Firstname" //provide state key
        onChange={onChange}
      />
      <InputContainer
        type="text"
        name="Middlename"
        placeholder="Middlename"
        value={Student.Middlename}
        stateKey="Middlename"
        onChange={onChange}
      />
      <button type="submit">Submit</button>
    </form>
  );
}
//make this a pure component (does not re render if nothing changed)
const InputContainer = memo(function InputContainer({
  type,
  name,
  placeholder,
  value,
  onChange,
  stateKey,
}) {
  const rendered = useRef(0);
  rendered.current++;
  return (
    <div>
      <div>{rendered.current} times rendered.</div>
      <input
        type={type}
        name={name}
        value={value}
        placeholder={placeholder}
        onChange={e =>
          //pass state key and new value to onChange
          onChange(stateKey, e.target.value)
        }
      />
    </div>
  );
});

ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Upvotes: 0

Danielo515
Danielo515

Reputation: 7051

Managing forms in react is a task complex enough to delegate it to a library. Alo, big forms are not a good candidate for functional components because the problems that you outlined. You can, of course, spend the time to tune it up, but I think the effort may not worth the benefit.

My personal recommendation is to try one of the many react form libraries out there. One that I personally like is Formik

If you want to manage the form yourself I recommend to encapsulate the form on stateful component and use the key property for easier reset when you need it.

Another alternative will be the usage of memoization, for example using react.memo. But that will not guarantee success unless your data has the proper shape. This means, simple values that can be compared between themselves, not arrays, not functions, not objects.

Upvotes: 2

Related Questions