while learning the concept controlled components in Reactjs I came across this statement
controlled component: "A component which renders a form, but the source of truth for that form state lives inside of the component state rather than inside of the DOM"
what is a form state? and what does it mean by form state living inside of DOM or component?
could you please elaborate on it?
what is a form state?
The form state just means "the current values entered into the individual input elements of a form". They can be accessed through the individual input elements value property or through the <form> elements form data itself.
what does it mean by form state living inside of DOM or component
It just means that if you do not explicitly store those values in react state and pass them to the <input> during render it will just be kept in the HTML input elements itself. If you inspect an <input> element in the developer console you will see, that it has a value prop. Uncontrolled just means that you do not explicitly pass that value to the <input> and that you do not handle changes to the value yourself. If you need to access that value in your code you would have to get a ref to the <input> and read that value.
function UncontrolledForm() {
const handleSubmit = event => {
event.preventDefault();
// values are stored in the form but they can be accessed through the DOM node
const formData = new FormData(event.target);
console.log(Object.fromEntries(formData.entries()));
};
return (
<form onSubmit={handleSubmit}>
<label>
foo
<input name="foo" />
</label>
<label>
bar
<input name="bar" />
</label>
<button>submit</button>
</form>
);
}
Controlled on the other hand means that you "control" the value yourself by passing it to the <input> during render all the time.
function ControlledForm() {
// values are kept in react state
const [values, setValues] = useState({foo: '', bar: ''});
const handleChange = event =>
setValues(state => ({
...state,
[event.target.name]: event.target.value
}));
const handleSubmit = event => {
event.preventDefault();
console.log(values);
};
return (
<form onSubmit={handleSubmit}>
<label>
foo
{/* we explicitly pass a value and onChange handler and manage the values ourself */}
<input name="foo" value={values.foo} onChange={handleChange} />
</label>
<label>
bar
<input name="bar" value={values.bar} onChange={handleChange} />
</label>
<button>submit</button>
</form>
);
}
Related
I want to submit two forms from a single button in next.js without using document.getElementById.
I am using two form tags and fetching their data in two different objects and I want them to be submitted at once with a single button.
I've tried doing this by document.getElementById.Submit() but it throws an error that document is not defined.
Similar question has been asked on SO but the solution offered is by using JQuery which I can't use.
const submitForms = function () {
useEffect(()=>
{
document.getElementById("form1").submit();
document.getElementById("form2").submit();
})
};
Please tell me where am I going wrong?
The basic principle is that you store your form data in state, update the state when the form information changes, and then when the button is clicked, submit the state data to the server endpoint that processes that data.
In this working example we use an object as the state, and assign each form an id (see data attributes). We attach listeners to the forms so that when any of the inputs change we can capture their events as they bubble up the DOM (event delegation).
In the handleChange function we take the id from dataset of the form (currentTarget), and the name and value from the element that was changed (target) - more on the key differences here - and then update the state using that information.
handleClick then stringifies the state, at which point you can submit it to the server endpoint.
It may benefit you to revisit how React works. It has its own way of updating the DOM which means you shouldn't be using any native DOM methods.
const { useEffect, useState } = React;
function Example() {
// Set the state
const [ forms, setForms ] = useState({});
function handleChange(e) {
// Destructure the id from the dataset of the form
const { dataset: { id } } = e.currentTarget;
// Destructure the name and value from the
// element that changed
const { name, value } = e.target;
// Update the state. We preserve the existing
// state data using the spread syntax, and set the new
// value of the property that has the id as key.
// That new value is an object representing the old
// value data (again using the spread syntax), and
// updating the property using the element name and value.
setForms({
...forms,
[id]: { ...forms[id], [name]: value }
});
}
function handleClick() {
// Stringify the data. You can now use
// `fetch` or something like Axios to send the
// data to the server
console.log(JSON.stringify(forms));
}
return (
<div>
<form data-id="formone" onChange={handleChange}>
<fieldset>
<legend>Form 1</legend>
Name: <input name="name" />
<br/>
Age: <input name="age" />
</fieldset>
</form>
<form data-id="formtwo" onChange={handleChange}>
<fieldset>
<legend>Form 2</legend>
Role: <input name="role" />
</fieldset>
</form>
<button
type="button"
onClick={handleClick}
>Submit all form data
</button>
</div>
);
};
ReactDOM.render(
<Example />,
document.getElementById('react')
);
form, button { margin-top: 1em; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Additional documentation
Spread syntax
Destructuring assignment
you can use a function and when form submitted, use in first line of your function:
e.preventDefault()
And when click on another button, call this function and when form is submitted also run this function
Don't use DOM function when work with ReactJs or NextJs
I'm building a mortgage calculator app and I want to have it update in real-time in front of the user. To do this I need to update state whenever input is changed so that the component rerenders.
I plan to do this using the onChange event on each input field, my question is should the onChange event call a different function for each input to update that state property, or is there a way to have one function, and change the state property that is being updated.
For example here is my class Component with the form, state, and onInputChange function
class Calculator extends Component {
state = {
price: 250000,
term: 20
};
onInputChange = (event) => {
this.setState({ price: event.target.value })
};
render() {
return (
<div>
<form>
<label htmlFor="propertyPrice">Property Price</label>
<input
type="number"
id="propertyPrice"
name="propertyPrice"
value={this.state.price}
onChange={this.onInputChange}
/>
<label htmlFor="term">Mortgage Term</label>
<input
type="term"
id="term"
name="term"
value="3"
/>
</form>
</div>
);
}
}
As you can see on the Property Price input I'm using the onChange event to call the function onInputChange, which directly updates the price in the state. Do I need a new function for the mortgage term input, or can I use the one function and just change which state property I'm updating, if so, how can I determine which state property is being updated in the function?
I do it this way:-
Create a single onInputChange() like this
onInputChange = (name, value) = {
this.setState({[name]: value})
}
Now create any number of inputs but make sure your onChange handler receives a function like this
onChange = {(e) => onInputChange(nameOfInput, e.target.value)}
Make your form input tags name same as state name and then you can add this for every input's onChange handler.
const handleChange = (e) => {
this.setState({[e.target.name]: e.target.value});
}
I have a search text box I need to get the value onchange send the request to API when I use the normal event.target method it shows error. how to rectify it as in onchange I need to call a function with some arguments so I cannot go by ease.
my text box is :
<input className="ReactSearchBox" name="search" placeholder="Search Doctors"
onClick={() => {
this.onProviderListing(this.state.skip,0);
this.onEnable();
}}
onChange={() =>this.onSearchProvider(this.state.skip,0)} />
my function where i need the onchange value is:
onSearchProvider(nSkip,nLimit,e){
this.setState({
limit:nLimit,
skip:nSkip,
listing: this.state.listing,
searching: !this.state.searching
})
//console.log(nlimit);
var headers = {
"Content-Type":"application/json",
"AccessToken":localStorage.TOKEN,
}
var _calObj = {initiatorId:localStorage.userid,visitType: "office", skip:nSkip,limit:"5", includeOfflineProviders:"true",searchQuery:"lo"}
I need to give my input values in search query onchange correspondingly, sort it out plz.
You're not passing event from input. Change the onChange prop to:
<input className="ReactSearchBox" name="search"
placeholder="Search Doctors"
onClick={() => {
this.onProviderListing(this.state.skip,0);
this.onEnable();
}}
onChange={(e) =>this.onSearchProvider(this.state.skip,0, e)}
/>
onSearchProvider(nSkip,nLimit,e){
const value = e.target.value; // text box value
}
You can update the onChange in jsx by passing the e event object also like:
onChange={(e) => this.onSearchProvider(this.state.skip,0,e)}
and in onSearchProvider you can access it like:
onSearchProvider(nSkip, nLimit, {target}){
// you can see search box text here on change
console.log(target.value)
}
You don't need to pass state values on onChange method call.
State will be consistent throughout component.
<input className="ReactSearchBox" name="search" placeholder="Search Doctors"
onChange={() =>this.onSearchProvider(0)}
/>
And you can get value from event of that input as event.target.value
onSearchProvider(nLimit,e){
// access your state values directly here like this.state.skip
const searching = e.target.value;
}
How to clear the value inside the input in function Admin after I click the "Add" button? Should i use another class based component instead of a functional component?
I have set the value of one of the input box as : value={props.item} and in the this.setState I update the value of item as item:"".
AddInfo(info){
let s = this.state.products;
let obj ={name:""};
obj.name=info.productName;
s.push(obj);
this.setState({
products:s,
item:"" //Here i set the value of item equal to an empty string.
})
console.log(this.state.products);
}
function Admin(props){
let productName="";
return (
<div>
<input type="text" required placeholder="Product Name" onChange={(e)=>{productName=e.target.value}} value={props.item}></input><br/>
<button type="Submit" onClick{(e)=>props.AddInfo({productName})}>Add</button>
</div>
)
}
You have to save your input within a local state of the input function:
AddInfo(info){
let s = this.state.products;
let obj ={name:""};
obj.name=info.productName;
s.push(obj);
this.setState({
products:s,
})
console.log(this.state.products);
}
function Admin(props){
const [productName, setProductName] = useState('');
return (
<div>
<input type="text" required placeholder="Product Name" onChange={(e)=> setProductName(e.target.value) value={productName}></input><br/>
<button type="Submit" onClick{(e)=> {props.AddInfo({productName}); setProductName('')}}>Add</button>
</div>
)
}
This will work for you, since you are not mutating the productName variable anymore but now you are saving it in a local state of that input function.
Hope this helps!
Admin is like a form, and the main decision you have to make is rather you want it to be controlled (info is stored in stated, and state is reflected in the ui), or uncontrolled (info is taken from the dom once 'Add' is clicked.
Since you want to empty the input once 'Add' is clicked it makes sense to make this component controlled.
The next decision is rather you want it to be a functional component, or a class component. In nowadays it doesn't really matter (functional components can now use state with the state hook).
To store state in you functional component use React's useState hook.
function Admin({addInfo}){
const [productName, setProductName] = useState(")
return (
<div>
<input
type="text"
placeholder="Product Name"
onChange={(e)=>{
setProductName(e.target.value)
}
value={prodcutName}>
</input>
<button
onClick{(e)=>{
props.addInfo({productName})
setProductName("") // Will set the input to an empty string
}
>
Add
</button>
</div>
)
}
With the following method:
handleClick(event) {
const inputText = this.refs.inputText
console.log(inputText.value.trim())
}
I am trying to get Material-UI's <TextField/> to return the input text correctly with ref like the <input/> can with <button/> triggering it:
<input
className='form-control'
placeholder='Input Text'
ref='inputText'
type='text'
/>
<button
onClick={(event) => this.handleClick(event)}
>
And I attempted the following with <TextField/>, but it returns as undefined. How can I get it to return inputted text correctly like the <input/> above?
<TextField
hint='Enter text'
className='form-control'
ref='inputText'
type='text'
/>
I would suggest this approach:
Set up your textfield with a value and onChange function that are hooked into redux itself, where the onChange function just updates the value.
So you'd have something like this :
<TextField
value={this.props.textFieldValue}
onChange={this.props.textFieldChange}
Where the textFieldChange is an action that simply updates the textFieldValue. Most forms in redux will work something like this. Keep in mind the names i made up for those props and action are just for example. If you have a big form you might want to consider have part of the state tree dedicated to the form itself where you have :
state: {
form: {
textField: ...your textfield value here,
name: ...,
whateverElse: ...
}
};
I like doing this with redux because I can make that architect form part of the state to look like the json payload of wherever I'm sending it to, so there I can just send the form went I want to send it.
Anyways, back to this example. When you click your handleClick now. All you need to do is this to get the value:
handleClick(event) {
console.log(this.props.textFieldValue.trim());
}
Because the textfield is updated with every change, you always have access to it in your state. This also gives you flexibility over the refs approach, because if you use refs you will have a lot harder of a time getting access to that form in other components. With this approach, all the information is on your state so you can access it anytime, as long as you manage your props.
You should use the onChange={} to get the value:
_onChange = (e) => {
console.log(e.target.value);
}
<TextField
onChange={this._onChange}
/>
Here's a better solution than using onchange event, we get directly the value of the input created by material-ui textField :
create(e) {
e.preventDefault();
let name = this.refs.inputText.input.value;
alert(name);
}
constructor(){
super();
this.create = this.create.bind(this);
}
render() {
return (
<form>
<TextField ref="inputText" hintText="" floatingLabelText="Your name" /><br/>
<RaisedButton label="Create" onClick={this.create} primary={true} />
</form>
)}
hope this helps.