I am new to react, I am not clear about the relationship between onChange and value in input tag. If onChange has update any variable that is declared above using useState with empty value, it causes error. Why does it happen?
I think example code makes my question clear so here is an example.
from React, { useState } import 'react'
function example() {
const [example,setExample] = useState('');
return (
<input type="text" onChange={e=> setExample(e.target.value} value={} />
)
}
In input tag, there is no specific variable as value attribute. If I put example as value, it works properly. Why does it act like it? What I thought was since the example has declared at above, no matter what value is, it should work properly.
Thanks in advance :)
In React everything is object. The HTML structure you see in the return is also an object and input object needs a key value for updating the actual value.
To understand this you can consider your input like this
input {
value : <VALUE>
onChange : <YOUR_FUNTION_THAT_UPDATES_VALUE>
}
To map the values in the input you have to assign the value as {example} in the input object and any change in the value will re-render your input object.
from React, { useState } import 'react'
function example() {
const [example,setExample] = useState('');
return (
<input type="text" onChange={e=> setExample(e.target.value} value={example} />
)
}
React, as word suggest reacts to any event which occurs in your web page, now when a change is occurred in the text of the input, we need to update our state to hold the value that use just typed in for future uses.
Also we need value in our input field to be able to post it, that why we use value in value property like
value={value}
and in order to update the value variable as user types in, we hook up an event to update value (State) whenever anything changes.
Related
I am trying to validate an email TextField with a button. I am using props to store the email TextField's error state.
Email Textfield code:
<TextField
required
error={values.emailInvalid}
id="emailAddress"
name="emailAddress"
label="Email Address"
onChange={handleChange("email")}
defaultValue={values.email}
fullWidth
autoComplete=""
variant="standard"
/>
Button code:
var re = /\S+#\S+\.\S+/;
var testEmail = re.test(this.props.values.email);
if (this.props.values.email.length === 0 || !testEmail) {
this.props.values.emailInvalid = true;
} else {
this.props.values.emailInvalid = false;
//go next page
}
console.log(this.props.values.emailInvalid);
The "emailInvalid" boolean is updating properly according to the console output, but the TextField does not visibly show the red error state is "emailInvalid" is set to true.
You cannot just assign the value of emailInvalid with this.props.values.emailInvalid = true.
If values is a state object, you need to also pass the setState function in props and use it like below
this.props.setState({ emailInvalid:true })
Also, you should use this.props.values instead of values in the TextField component
You need to use setState to mutate the component state for example
SetEmailInvalid(true);
for Class based components:
this.setState({emailInvalid: true});
You can't edit react state directly. Open This Link To See Why
The way you are changing things in React is not supported. you can follow this guide:
First of all: PROPS ARE READONLY and you can not change them.
Second, for changing things that have to be shown in the browser as it changes is done via State. to fix your error first import useState like: (we are using states as hook)
import {useState} from "react";
then you have to use your state and initialize the amount:
const [emailIsValid, setEmailIsValid] = useState(true)
this will set the initial value of the emailIsValid to true.
now whenever you want to change the value of emailIsValid , you have to call the set function and set the value like:
setEmailIsValid(false)
now you can use the emailIsValid anywhere in your react component.
I am using the following code to render a Material UI AutoComplete field with options retrieved from an API. I would like to prevent re-rendering of the input field but the chosen country is used as a prop for the Country component which should be updated onChange. set_alpha3Code doesn't seem to update the state from within useCallback. How do I get around that?
let AC = memo(AutocompleteCountries)
function Show(props: {})
{
let [alpha3Code_, set_alpha3Code_] = useState<string>('gbr');
let onChange = useCallback((alpha3Code) => {
console.log(alpha3Code_);
set_alpha3Code_(alpha3Code);
}, []);
return (
<div>
<AC onChange={onChange}/>
{alpha3Code_ ? <Country cca3_={alpha3Code_}/> : null}
</div>
)
}
Two things jump out about that code:
onChange without value
Stale state
onChange without value
In the normal case, if you specify onChange with an input control, you have to specify value to tell the control what the current (updated) value is. The documentation for Material-UI's Autocomplete definitely suggests you need both:
Controlled states
The component has two states that can be controlled:
the "value" state with the value/onChange props combination. This state represents the value selected by the user, for instance when pressing Enter.
The same thing with a standard input prevents the input from ever changing. I haven't used the Material-UI Autocomplete, but I suspect it's similar.
Stale state
Your code is updating the value of alpha3Code_ (if onChange is called with a new value), but the console.log in that code looks at at an old one:
let onChange = useCallback((alpha3Code) => {
console.log(alpha3Code_); // <−−− Uses the *first* `alpha3Code_` only
set_alpha3Code_(alpha3Code);
}, []);
// ^^−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− Because you haven't included
// `alpha3Code_` in these dependencies
But even though that console.log shows you outdeated information, the rest of the code is fine and will work. It will re-render the AC with the updated code.
If you want to see the alpha3Code_ used for rendering, put a breakpoint on the return and look at it in the debugger (or move the console.log outside the onChange handler to above the return).
How can i optimize 'form' rendering. For each key pressed, the component is rendered
Any idea to solve or improve it?
const Example = () => {
const [inputForm, setInputForm] = useState('');
const inputHandler = event => {
setInputForm(event.target.value);
};
console.log('Rendering');
return (
<div>
<div>
<span>Text: {inputForm}</span>
<input value={inputForm} onChange={inputHandler} />
</div>
</div>
);
};
log of component rendering
Thanks guys!
Try to use Uncontrolled Components, from the documentation:
https://pt-br.reactjs.org/docs/uncontrolled-components.html
As #andergtk mentioned, if you want to avoid rendering the input on each key press you'll have to resort to an input that React does not control.
To write an uncontrolled component, instead of writing an event
handler for every state update, you can use a ref to get form values
from the DOM.
but the rendering on every key press that you notice is expected in the case of a controlled input where React has control over the value of the field and should not worry you
A controlled input is more "powerful" because you let React sync your data with your input value
more on this subject in the docs or in this article (there are lots of other resources on this): https://goshakkk.name/controlled-vs-uncontrolled-inputs-react/
improving your case is not about stop rendering, every time input value is changed it needs to be re-rendered, if not changes wouldn't apply
to improve performance you may:
useCallback to the event handler if you gonna do calculations there
maybe split label out of the component returning only input element
useState may be declared outside Input component to make sense
Input props: { value, setValue } ,. then setup callback
react renders its components based on props or state changes, so always something changes on screen react has to re-render that component
I have a TextField that I only render when another radiobutton is clicked. According to the React and Material-UI documentation, I should be able to get a ref to an input element inside a Mui TextField using inputRef={this.myRef} on the TextField. I'm able to do this for another TextField that is not toggled, but when I try that the with TextField that I just turn on, it shows null.
I've tried using inputRef={this.otherText} and inputProps={{ref: this.otherText}} and same result.
// Start of my class where I do the createRef()
class Form extends Component {
constructor(props) {
super(props);
this.otherText = React.createRef();
}
// Start of function where I try to reference the ref:
processApplication = e => {
if (e.target.value.toLowerCase() === 'other') { // this triggers the TextField to be rendered
console.log(this.otherText); // Returns null, kinda - see screenshot
}
// The TextField I'm trying to reference:
<TextField
id='applicationOther'
name='applicationOther'
label='Describe application:'
margin='normal'
multiline={true}
fullWidth={true}
onChange={this.anyChange}
autoFocus={true}
inputRef={this.otherText} // Here's where the reference is made
/>
I expect this.otherText to have a reference to the element, but this.otherText.current is null.
So to add some content to an input of any tipe, including Material TextField, you'd assign it as a value, for instance:
this.state = { input: 'Some string' }
<TextField value={this.state.input} />
Keep in mind the slight difference between uncontrolled and controlled components, so depending on your use case, you may want to pass defaultValue instead of value. From the docs:
In the React rendering lifecycle, the value attribute on form elements will override the value in the DOM. With an uncontrolled component, you often want React to specify the initial value, but leave subsequent updates uncontrolled. To handle this case, you can specify a defaultValue attribute instead of value.
Docs link
I have had same issue, doing following worked for me:
<TextField
variant="filled"
inputRef={(input) => {
if(input != null) {
input.focus();
}
}}
/>
The trick here was if(input != null) in my case. You can also take a look at working example here: CodeSandBox- Material-ui-TextFieldFocus
I have an input tag in render like this:
<input id="time" type="time">
and I need dynamimically add value attribute
How can I do this in React? Thanks in advance
Yes, you can do. e.g. props is an object which contains props or properties that you want to add based on some condition then you can something like
const props = { id: 'time', type: 'time' };
if (condition1) {
props.value = 'some value';
}
if(condition2) {
props.abc = 'some other value';
}
<input {...props} >
You should add an onchange attribute on the input which changes the state of your component. This will look something like this:
<input type="text" value={this.state.value} onChange={this.handleChange} />
The handleChange method should then look something like this:
handleChange = (event) => {
this.setState({value: event.target.value});
}
For more information check out the following article: https://reactjs.org/docs/forms.html
You would need to set the state of React. So...
this.setState({
myValue: newValue
})
Then, inside render:
<input id="time" type="time" value={this.state.myValue}>
This assumes you have your state setup with a constructor. Which is a whole other can of worms.
You shouldn't dynamically add or remove a "value" field. When you create a React input, it must be either "controlled" or "uncontrolled" through its whole lifespan. Changing it will make React yell a warning on the console.
React understads an input is meant to be uncontrolled if value is not present or undefined, so you need to at least set it to "" in order to achieve a controlled empty input.
"Controlled" means react controls its value. Ex: For the value to be changed, you'd need to notify react of the change (through onChange + setState) and then make it change its value;
"Uncontrolled" means react can't control the input's value, and you'd read and change the value through regular DOM ways (ex: input.value).
That being said, in order to dynamically change the presence of any element properties (props), you can use the "object spread" operator.
function render() {
const custom = { value: 2, color: randomColor() }
return <Element {...custom}/>
}
I guess you are rendering the tag within a render() function within a JSX expression (otherwise you would have added it through normal Javascript or JQuery).
Therefore the JSX expression will have something like:
<input id="time" type="time" value={yourValue}>
making sure that yourValue is in scope within the context of execution of your render() in your ReactComponent class or in the props. Alternatively it could be in the state.