is value attribute necessary in a react component's input? - reactjs

My Register component:
const Register = () => {
const [name, setName] = useState('');
const onNameChange = (e) => {
setName(name = e.target.value);
};
<form className="form" onSubmit={onFormSubmit}>
<div className="form-group">
<input
type="text"
placeholder="Name"
name="name"
value={name}
onChange={onNameChange}
required
/>
</div>
</form>
My question is what is the purpose of putting the value attribute i.e "value={name}"?
I noticed that even without writing it(the value attribute), the value(e.target.value) is taken as whatever is over the said <input>. It seems to me that it functions the same without the value attribute.

Setting a value attribute isn't necessary, but it's usually a good idea. Putting a value attribute makes the input controlled, which means that the value in the input will always exactly correspond to what's in the React state, which will make the program's logic easier to reason about.
With a controlled input, if you ever need to set the value to something other than what the user has typed in, you just need to call setName with the value to set, and the input will receive that new custom value.
If you use an uncontrolled component (one without a value), you'll probably want to use a ref to get the value (since having name / setName as stateful doesn't really provide a benefit anymore).

Related

How is React handling renders for this uncontrolled input element?

I'm going through React's beta docs and would like to better understand how react handles user's interactions with the UI. Specifically with this non-controlled input field.
Note:
the input field is not disabled
new input values should not trigger a render, because it's not changing any state (local variables are ignored)
So, then why is the change not reflected in the UI???
The code is bellow, and an interactive "sandbox" can be found here: https://beta.reactjs.org/learn/state-a-components-memory under challenge #2 at the bottom of the page.
export default function Form() {
let firstName = '';
let lastName = '';
function handleFirstNameChange(e) {
firstName = e.target.value;
}
function handleLastNameChange(e) {
lastName = e.target.value;
}
function handleReset() {
firstName = '';
lastName = '';
}
return (
<form onSubmit={e => e.preventDefault()}>
<input
placeholder="First name"
value={firstName}
onChange={handleFirstNameChange}
/>
<input
placeholder="Last name"
value={lastName}
onChange={handleLastNameChange}
/>
<h1>Hi, {firstName} {lastName}</h1>
<button onClick={handleReset}>Reset</button>
</form>
);
}
To be clear, I'm not asking how to solve this problem; I know how to hook it up and fix it with state. I am interested in what's preventing the input field from updating with a new value.
On initial render it receives the empty string as its value. But then why doesn't it change when a user types?
Here's two non-react input fields, one with a value attribute, and one where the value is set by JS. Both can be modified:
<input type="text" value="editable">
<input type="text" id="input2">
<script>
document.getElementById('input2').value = "also editable"
</script>
So, why isn't the input editable in react?
There are two reasons for a component to render:
It’s the component’s initial render.
The component’s (or one of its ancestors’) state has been updated.
In your example, the second condition is not met and react has no way of knowing your input values changed, it does not re render, and so does not commit changes to the DOM.
Note that react is wrapping event handlers, it does not work exactly like vanilla JavaScript

what is onChange={(e) => setName(e.target.value)}? in React mean

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'));

Not getting any console.log data from first name field box

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.

React autoFocus attribute is not rendered

I'm trying to render an <input> field with autofocus:
<input type="text" autoComplete="off" autoFocus={true} className="form-control" />
But it's being rendered as:
<input type="text" autocomplete="off" class="form-control">
And the input does not focus.
Why is that? How can I get the autofocus attribute to render?
This is expected, actually. React will manually call focus() on the relevant element, but it will not add the attribute itself.
Here is a quote from Dan Abramov from a ticket response from ~2 years ago:
This is intentional because it works very differently in different browsers. So instead of actually setting a DOM attribute, we polyfill its behavior in JavaScript.
Also, from another thread:
Chrome respects dynamically added elements with autoFocus if there hasn’t been one before. Even creating an input with autoFocus, adding it to the document, and removing it in the next tick is enough to disable autofocusing of any elements added in the future in Chrome.
Firefox just doesn’t care about any dynamically added elements with autoFocus at all.
And Safari always respects them.
That being said, you could still force the attribute with autofocus="true" instead of autoFocus={true} but it might not actually work in a reliable fashion. After all, there is a reason the React team polyfilled this.
In other words, if you want a reliable, consistent cross-browser behavior use autoFocus, if you must have the native attribute you can use autofocus="true", but know that you'll be at the mercy of letting the browser decide when and how said element will be focused.
You can try using a ref, and setting the focus when your component is ready.
const YourComponent = (props) => {
const inputEl = useRef(null);
useEffect(() => {
inputEl.current.focus();
}, []);
return <div>
<input autofocus="true" ref={inputEl} />
</div>;
}
Answer by Brandon works perfectly. Additionally, if you want to autofocus on an input on a Modal for example, just keep track of the modal state and use that to focus. For example:
const refModal = useRef(null);
useEffect(() => {
if(modalOpen) {
refModal.current.focus();
}
}, [modalOpen]);

A single onChange listener on a <form> tag

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...

Resources