EN
React - form submit button in root component
2
answers
3
points
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
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
2 comments
Add comment
what if I want to handle state individually for each component. How to I go about that?
You should use useState() for each field and exchange state with use context() or events in child components
1
points
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
0 comments
Add comment