I am new to React;I am learning about React Form. I understand the code but I do not really understand the concept behind and why we use this line " onChange={(e) => setName(e.target.value)}".
Thank you for your help.
They used this example:
import ReactDOM from 'react-dom';
function MyForm() {
const [name, setName] = useState("");
const handleSubmit = (event) => {
event.preventDefault();
alert(`The name you entered was: ${name}`)
}
return (
<form onSubmit={handleSubmit}>
<label>Enter your name:
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</label>
<input type="submit" />
</form>
)
}
ReactDOM.render(<MyForm />, document.getElementById('root'));
You have two different things happening here.
Event (e)
First you have e which is short for event. To understand what it does change onChange={(e) => setName(e.target.value)} to onChange={(e) => console.log(e)}. Inspect the log and you'll find a list of events in which one of them is target. Open target (if not already opened) and within target, you'll find value. In short, this is the target value that's being typed in your input, it's what the user is typing.
useState
Your using state to track the value in your input. So [name] is the getter and [setName] is the setter. If you notice in your alert you have alert(The name you entered was: ${name}). The variable name is the getter which means it holds the current value of the state. On the other hand setName will set that value. It's able to do so because you're setting and tracking the value here on change onChange={(e) => setName(e.target.value)}.
means e is the event that is happening which here is change, target is the element that triggered the event, which here is the input, and value is the value of the input element Onchange is as the name suggests and setState is used to change the state you defined eariler you should read the documentation as well it would clear stuff up maybe an online explanation on youtube anyways all the best for your React journey
There are at least 5 concepts that I can think of being used in this single line that I recommend you to learn about
Higher-Order Functions
Anonymous functions
Arrow Functions
State
Synthetic Events
To start your journey, just know that this code onChange={(e) => setName(e.target.value)} is the same as the one below:
function clickHandler(e) {
setName(e.target.value)
}
<input
type="text"
value={name}
onChange={clickHandler}
/>
Maybe that makes things clearer.
I don't necessarily have an answer but I am here because I have been looking for the answer to this question and I feel that the people who have answered have missed what the question is really asking - or at least what I am looking for.
I found a similar code to this question while going through the MDN react guide tutorial and what made me curious is why use setUser setter instead of just using a local variable?
I guess the answer I am reaching is the setter setUser (setState) is used in order to re-render the component when the changed value is also represented in the component. However, in the above example the name is not actually used the component so there is no need for using setUser (or useState).
Check out this tweak to the code that works perfectly with a local variable:
import ReactDOM from 'react-dom';
function MyForm() {
//const [name, setName] = useState("");
var name = "";
const handleSubmit = (event) => {
event.preventDefault();
alert(`The name you entered was: ${name}`)
}
return (
<form onSubmit={handleSubmit}>
<label>Enter your name:
<input
type="text"
onChange={(e) => (name = e.target.value)}
/>
</label>
<input type="submit" />
</form>
)
}
ReactDOM.render(<MyForm />, document.getElementById('root'));
Related
import React, { useState } from "react";
import "../styles.css";
const FormBox = () => {
const [value, setValue] = useState({
fName: "",
lName: "",
});
const [textArea, setTextArea] = useState("");
const handleSumbit = (e) => {
e.preventDefault();
console.log(value);
console.log(textArea);
};
return (
<div className="center">
<form onSubmit={handleSumbit}>
<div>
{/*This is how to set a control form box */}
<input
placeholder="First Name"
type="text"
value={value.fName}
onChange={(e) => setValue(e.target.value)}
/>
</div>
<div>
{/*This is how to set a control form box */}
<input
placeholder="Last Name"
type="text"
value={value.lName}
onChange={(e) => setValue(e.target.value)}
/>
</div>
<div>
<textarea
value={textArea}
onChange={(e) => setTextArea(e.target.value)}
></textarea>
<div>
<input type="submit" />
</div>
</div>
</form>
</div>
);
};
export default FormBox;
I'm working on a basic React project and I'm currently getting the message "Warning: A component is changing a controlled input to be uncontrolled. This is likely caused by the value changing from a defined to undefined, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://reactjs.org/link/controlled-components
input"
Another problem is that I'm not getting any info when typing in first name field box. I do get back last name and textarea.
Thank you so much and happy holiday
You are having two issues:
Not getting info from the first name field
The uncontrolled vs controlled warning
Issue 1.
This actually happens because on both the first name and last name inputs you are doing setValue(e.target.value). setValue() sets the object that is originally initialized as { fName: '', lName: '' }. This means that even though the value is an object at first, when writing on any of the fields, it will become a simple string, either the first or last name.
This problem does not happen with the text area, since you keep track of its value on a separate state, textArea. That's one way to fix your issue, hold both fName and lName on separate useState calls. Another way is to replace the old value object with a new one that works as expected, for example:
<input
placeholder="First Name"
type="text"
value={value.fName}
onChange={(e) => setValue({ ...value, fName: e.target.value })}
/>
I would also suggest avoiding abbreviations whenever possible. You could've used firstName and lastName instead as variable names. It really goes a long way improving readability.
Issue 2.
This warning is a side effect of the first issue. It appears because you are using controlled inputs but when writing on one of them the object is turned into a string and both value.fName and value.lName are undefined. Controlled inputs are not supposed to have undefined as a value, since that's usually an indicator that you want to use uncontrolled inputs. The empty value should be an empty string, which was intended and will be fixed if you fix the first issue.
There's another stack overflow sharing the differences between controlled and uncontrolled components: What are React controlled components and uncontrolled components?
A Controlled Component is one that takes its current value through props and notifies changes through callbacks like onChange. A parent component "controls" it by handling the callback and managing its own state and passing the new values as props to the controlled component. You could also call this a "dumb component".
A Uncontrolled Component is one that stores its own state internally, and you query the DOM using a ref to find its current value when you need it. This is a bit more like traditional HTML.
The important bit to note here is that an uncontrolled component doesn't receive a value prop and hence its value is undefined.
Assuming there is a state in a functional component that holds a string; since this state is updated from multiple places in the component, is it possible to "intercept" all updates, and always perform some operation such as trimming the value before it is set? Would it be better to utilize useReducer here?
you would create a reusable function for that and apply it where it's needed:
const [name, setName] = useState('');
const updateName = (value) => {
// do some validation (like avoid numbers)
if(value.match(/[0-9]+/)) return
// some normalize
setName(value.trim());
}
return (
<>
<label for="name">Name</label>
<input id="name" value={name} onChange={e => updateName(e.target.value)} />
<button onClick={() => updateName('C3P0')}>Try droid name</button>
</>
)
or use some specific library that has built-in validations like yup
I have radio input use want to use ref to show the result on console.
I always get second value even when I have choose the first one.
This is the constructor
this.inputKelamin = React.createRef();
and rendered like this
<div>
Jenis Kelamin :
<input name="kelamin" type="radio" value="laki - laki" ref={this.inputKelamin}/>
Laki - laki
<input name="kelamin" type="radio" value="perempuan" ref={this.inputKelamin}/>
Perempuan
</div>
onSubmit, I put it like this via console :
alamat : ${this.inputAlamat.current.value}
the result is always "perempuan"
This is not for production, just a learning purpose, thank you
You're using same ref for both the element, second ref={this.inputKelamin} overrides the first one and it always points to the second radio button.
From official docs
Refs provide a way to access DOM nodes or React elements created in the render method.
You should create 2 different refs for both inputs.
And you're checking the wrong property here
this.inputAlamat.current.value
value will always be the attribute value you gave value="perempuan".
In case of radio you should look at the checked property, which tells you whether it was selected
this.inputKelamin.current.checked
Also, you might want to look at controlled and un-conntrolled components
In order to get just one data from multiple radio you cam simply do this :
Example with typescript :
import React, { useRef } from "react";
function Form() {
const inputKelamin = useRef() as React.MutableRefObject<HTMLInputElement>;
const inputLaki = useRef() as React.MutableRefObject<HTMLInputElement>;
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
const selectedRadio = inputKelamin.current.checked ? inputKelamin.current.value : inputLaki.current.value
console.log(selectedRadio);
}
return (
<form className="form" onSubmit={handleSubmit}>
Jenis Kelamin :
Laki - laki <input type="radio" value="laki - laki" ref={inputKelamin} /> <br />
Perempuan <input type="radio" value="perempuan" ref={inputLaki} /> <br />
<button type="submit">Submit</button>
</form>
);
}
export default Form;
I'm playing with React Hooks - rewriting a form to use hook concepts. Everything works as expected except that once I type any 1 character into the input, the input loses focus.
I guess there is a problem that the outside of the component doesn't know about the internal changes in the component, but how do I resolve this issue?
Here is the useForm Hook:
import React, { useState } from "react";
export default function useForm(defaultState, label) {
const [state, setState] = useState(defaultState);
const FormComponent = () => (
<form>
<label htmlFor={label}>
{label}
<input
type="text"
id={label}
value={state}
placeholder={label}
onChange={e => setState(e.target.value)}
/>
</label>
</form>
);
return [state, FormComponent, setState];
}
Here is the component that uses the Hook:
function App() {
const [formValue, Form, setFormValue] = useForm("San Francisco, CA", "Location");
return (
<Fragment>
<h1>{formValue}</h1>
<Form />
</Fragment>
);
}
While answer by Kais will solve the symptoms, it will leave the cause unaddressed. It will also fail if there are multiple inputs - which one should autofocus itself on rerender then?
The issue happens when you define a component (FormComponent) inside the scope of another function which is called each render of your App component. This gives you a completely new FormComponent each time your App component is rerendered and calls useState. That new component is then, well, without focus.
Personally I would feel agains returning components from a hook. I would instead define a FormComponent component, and only return state from useForm state.
But, a working example closest to your original code could be:
// useForm.js
import React, { useState } from "react";
// Define the FormComponent outside of your useForm hook
const FormComponent = ({ setState, state, label }) => (
<form>
<label htmlFor={label}>
{label}
<input
type="text"
id={label}
value={state}
placeholder={label}
onChange={e => setState(e.target.value)}
/>
</label>
</form>
);
export default function useForm(defaultState, label) {
const [state, setState] = useState(defaultState);
return [
state,
<FormComponent state={state} setState={setState} label={label} />,
setState
];
}
// App.js
import useForm from "./useForm";
export default function App() {
const [formValue, Form] = useForm("San Francisco, CA", "Location");
return (
<>
<h1>{formValue}</h1>
{Form}
</>
);
}
Here's a sandbox
When you enter any text in input box. Parent Component is also re-rendering. So you need to make focus on input manually.
For this, use autoFocus in input tag
<input
type="text"
id={label}
value={state}
placeholder={label}
onChange={e => setState(e.target.value)}
autoFocus
/>
The above answers didn't work for me. The solution that worked for me was much simpler and, for that reason, less obvious.
The Problem
Essentially, the value that I was changing with the input was also being used for each key in a list of inputs.
Hence, when I updated the value the key would change and React would detect that it's different relative to the last key and create a new input in its place. As a new input it wouldn't focus on itself.
However, by using autoFocus it would automatically focus on the newly created input. But the issue wasn't fixed as it was obvious that the input was continually going through a cycle of un-focus and focus.
Here's an article demonstrating the problem.
The Fix
Update the key to an unchangeable value so React would have a stable reference to the list items. In my case I just updated it to the index. This is not ideal (React docs recommend using a stable ID), but in my situation it was okay because the order of the items won't change.
The first solution actually worked for me , initially the functional component which contained the text field was part of the main functional component , i was facing the same issue but when i extracted the text-field component into another page and imported it it worked fine
This was my component
<Paper >
<div>
<div style={{padding:'10px' ,display:'flex'}} >
<inputstyle={{marginRight:'5px'}} value={val} onChange={(e)=>{setVal(e.target.value)}} />
</div>
</div>
</Paper>
I was just playing a bit with a more generic way of dealing with form data, and figured that setting a single onChange listener on the wrapping form tag should work for all changes to input fields.
And indeed, it does - no surprise there. Events get called, I know what changed by inspecting event.target.name and can update state accordingly. Fully as expected.
However, React doesn't seem to like it and spams the well known "You provided a value prop to a form field without an onChange handler" console warning.
Would there be any other reason for not doing this, apart from the console warning? It seems to eliminate a lot of duplication React otherwise gets criticised about.
class App extends Component {
state = {
name: 'Default Name',
number: 12,
}
handleChange = (event) => {
this.setState({
[event.target.name]: event.target.value,
})
}
render() {
const { crashLogo } = this.props;
return (
<div className="App">
<form onChange={this.handleChange}>
<input type="text" name="name" value={this.state.name} />
<input type="number" name="number" value={this.state.number} />
</form>
</div>
);
}
}
Just for clarity: I'm not asking for alternative solutions, I know I can set the same change listener directly on every input, use redux-form, own wrapper components with context magic and so on...