Languages
[Edit]
EN

React - optimal way to create form (uncontrolled components)

6 points
Created by:
Majid-Hajibaba
612

In this article, we would like to show you an optimal way to create a form in React.

The approach used in the below example is useful when we want to create a form with a big amount of fields avoiding adding change handlers or references to each one field separately. The solution works even form fields are nested in other components.

The main idea of the below approach is to use object property paths as field names that are used to generate data object on form submit event or to bind fields for indicated form data.

The concept of the below article is to provide Form component that binds/gets field values automatically what looks following way when we use it (all useful logic is composed inside Form component):

const handleSubmit = data => {
    // some code here...
};
return (
  <div>
    {/*  Form component definition in the below code  */}
    {/*                                               */}
    <Form data={data} onSubmit={handleSubmit}>
      <input type="text" name="user.username" />
      <input type="password" name="user.password" />
      <input type="text" name="report.title" />
      <input type="text" name="report.goal" />
      <button type="submit">Submit</button>  
    </Form>
  </div>
);

Notes:

  • go to this article to read more about setProperty function (setObjectProperty),
  • go to this article to read more about getProperty function (getObjectProperty).

 

Practical example

// ONLINE-RUNNER:browser;

// Note: Uncomment import lines during working with JSX Compiler.
// import React from 'react';
// import ReactDOM from 'react-dom';
  
const getProperty = (object, path) => {
  	if (object == null) { // undefined or null
    	return object;
    }
  	const parts = path.split('.');
	for (let i = 0; i < parts.length; ++i) {
      	if (object == null) { // undefined or null
        	return undefined;
        }
      	const key = parts[i];
    	object = object[key];
    }
  	return object;
};

const setProperty = (object, path, value) => {
    const parts = path.split('.');
    const limit = parts.length - 1;
    for (let i = 0; i < limit; ++i) {
        const key = parts[i];
        object = object[key] ?? (object[key] = {});
    }
    const key = parts[limit];
    object[key] = value;
};
  
const iterateElements = (form, callback) => {
    const elements = form.elements;
    for (let i = 0; i < elements.length; ++i) {
        const element = elements[i];
        const tag = element.tagName;
        if (tag === 'INPUT' || tag === 'SELECT' || tag === 'TEXTAREA') {
            callback(element);
        }
    }
};

const getData = (form) => {
  	const data = {};
  	iterateElements(form, element => {
    	setProperty(data, element.name, element.value);
    });
  	return data;
};

const bindData = (form, data) => {
  	iterateElements(form, element => {
        const value = getProperty(data, element.name);
        element.value = String(value ?? '');
    });
};

const Form = React.forwardRef(({data, children, onSubmit}, ref) => {
    const formRef = ref ?? React.useRef();
    React.useEffect(() => {
		const form = formRef.current;
      	if (form) {
          	bindData(form, data ?? {});
        }
    }, [data]);
    const handleSubmit = e => {
        e.preventDefault();
      	if (onSubmit) {
      		onSubmit(getData(formRef.current));
        }
    };
    return (
      <form ref={formRef} onSubmit={handleSubmit}>
        {children}
      </form>
    );
});

// Usage example:
  
const areaStyle = { border: '1px solid gray' };
  
const App = () => {
  	const data = {
        user: {
            username: 'john',
            password: 'Secret$$'
        },
      	report: {
        	title: 'My raport title',
          	goal: 'My raport goal'
        }
    };
    const handleSubmit = data => {
        const json = JSON.stringify(data, null, 4);
        console.clear();
        console.log(json);
    };
    return (
      <div>
        <Form data={data} onSubmit={handleSubmit}>
          <div style={areaStyle}>
            <div>User:</div>
            <div>
              <label>Username: </label>
              <input type="text" name="user.username" />
            </div>
            <div>
              <label>Password: </label>
              <input type="password" name="user.password" />
            </div>
          </div>
          <div style={areaStyle}>
            <div>Report:</div>
            <div>
              <label>Title: </label>
              <input type="text" name="report.title" />
            </div>
            <div>
              <label>Goal: </label>
              <input type="text" name="report.goal" />
            </div>
          </div>
          <button type="submit">Submit</button>  
        </Form>
      </div>
    );
};
  
const root = document.querySelector('#root');
ReactDOM.render(<App />, root);

 

It is possible to get form data from outside of Form component in the following way:

// ...

const App = () => {
  	const formRef = React.useRef();
  	// ...
  	// It is possible to get data from outside of the Form component when reference is ready:
  	//   const data = getData(formRef.current);
    // It is possible to bind data too:
    //   bindData(formRef.current, data);
    return (
      <div>
        <Form ref={formRef} data={data} onSubmit={handleSubmit}>
          {/* ... */}
        </Form>
      </div>
    );
};
  
const root = document.querySelector('#root');
ReactDOM.render(<App />, root);

 

Alternative titles

  1. React - form with large amount of fields
  2. React - form with large number of inputs
Donate to Dirask
Our content is created by volunteers - like Wikipedia. If you think, the things we do are good, donate us. Thanks!
Join to our subscribers to be up to date with content, news and offers.

ReactJS

React - optimal form (uncontrolled components)
Native Advertising
🚀
Get your tech brand or product in front of software developers.
For more information Contact us
Dirask - we help you to
solve coding problems.
Ask question.

â€ïžđŸ’» 🙂

Join