Languages

React - form submit button in root component

3 points
Asked by:
kwartz
1060

How do I implement submit button on the root component which has several child components that has a part of a form?

function Root(props) {
    return (
      <>
        <div>
          <Component1 /> 
          <Component2 /> 
          <Component3 /> 
          <Component4 /> 
          <div>
            <button className="button" type="submit">
              submit
            </button>
          </div>
        </div>
        </>
    );
};

ReactDom.render(<Root/>, div);
2 answers
4 points
Answered by:
kwartz
1060

In this article you should find the solution.

Finally you can use it this way:

// 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 UserArea = () => {
    return (
      <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>
    );
};
  
const ReportArea = () => {
    return (
      <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>
    );
};
  
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}>
          <UserArea />
          <ReportArea />
          <button type="submit">Submit</button>  
        </Form>
      </div>
    );
};
  
const root = document.querySelector('#root');
ReactDOM.render(<App />, root);

 

Sources

  1. React - optimal way to create form (uncontrolled components) 
2 comments
kwartz
what if I want to handle state individually for each component. How to I go about that?
Root-ssh
You should use useState() for each field and exchange state with use context() or events in child components
Add comment
1 points
Answered by:
kwartz
1060

The solution that stores each field in the separated state:

// ONLINE-RUNNER:browser;

// Note: Uncomment import lines during working with JSX Compiler.
// import React from 'react';
// import ReactDOM from 'react-dom';
  
const areaStyle = { border: '1px solid gray' };

const TextInput = ({ label, value, onChanged }) => {
    const [localValue, setLocalValue] = React.useState(value);
    React.useEffect(() => setLocalValue(value), [value]);
    const handleChange = e => setLocalValue(e.target.value);
    const handleBlur = e => onChanged?.(localValue);
    return (
        <label>
          <span>{label}</span>
          <input type="text" value={localValue} onChange={handleChange} onBlur={handleBlur} />
        </label>
    );
};

const UserArea = ({ username, password, onUsernameChanged, onPasswordChanged }) => {
    return (
      <div style={areaStyle}>
        <div>User:</div>
        <TextInput label="Username:" value={username} onChanged={onUsernameChanged} />
        <br />
        <TextInput label="Password:" value={password} onChanged={onPasswordChanged} />
      </div>
    );
};
  
const ReportArea = ({ title, goal, onTitleChanged, onGoalChanged }) => {
    return (
      <div style={areaStyle}>
        <div>Report:</div>
        <TextInput label="Title:" value={title} onChanged={onTitleChanged} />
        <br />
        <TextInput label="Goal:" value={goal} onChanged={onGoalChanged} />
      </div>
    );
};
  
const App = () => {
    const [username, setUsername] = React.useState('');
    const [password, setPassword] = React.useState('');
    const [title, setTitle] = React.useState('');
    const [goal, setGoal] = React.useState('');
    const handleFormSubmit = e => {
        e.preventDefault();
        // add some logic here ...
    };
    const handleUsernameChanged = value => {
        setUsername(value);
        console.log(value);
    };
    const handlePasswordChanged = value => {
        setPassword(value);
        console.log(value);
    };
    const handleTitleChanged = value => {
        setTitle(value);
        console.log(value);
    };
    const handleGoalChanged = value => {
        setGoal(value);
        console.log(value);
    };
    return (
      <div>
        <form onSubmit={handleFormSubmit}>
          <UserArea
              username={username}
              password={password}
              onUsernameChanged={handleUsernameChanged}
              onPasswordChanged={handlePasswordChanged}
          />
          <ReportArea
              title={title}
              goal={goal}
              onTitleChanged={handleTitleChanged}
              onGoalChanged={handleGoalChanged}
          />
          <button type="submit">Submit</button>  
        </form>
      </div>
    );
};
  
const root = document.querySelector('#root');
ReactDOM.render(<App />, root);

 

See also

  1. React - form with useState example (controlled components)

  2. React - change state from props (functional component)

  3. React - form example (uncontrolled components)

0 comments Add comment
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.
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