Dynamically add attributes in React render input - javascript

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.

Related

How to manipulate object property on a reusable component in React

Im using MUIs input field for a lot of input sections of my questionnaire and want to save it to my state variable that is an object which holds all my form values. How can I manipulate my formData object in a reusable MUI component?
Im currently passing formData as a prop to the component but am unsure how to use the setFormData inside the component since it will be a different property each time I use it.
I was thinking each question is a property of the state object formData so itll look like
formData{question1: 'foo', question2: 'bar'}
This is how the form looks at the moment (i dont mind changing the structure if it makes sense to do so
Take a look at InputField on the form (i also attached the component code)
<QuestionLabel>Do you have a "Nickname":</QuestionLabel>
<InputField value={props.formData.nickname}/>
<QuestionLabel>Preferred Language:</QuestionLabel>
<InputField value={props.formData.language}/>
<QuestionLabel>Occupation:</QuestionLabel>
<InputField value={props.formData.occupation}/>
This is how the component looks (im aware i will have to change this)
export default function InputField(props){
return(
<TextField
fullWidth
value={props.value}
variant='standard'
/>
)
}
Disclaimer
First post so sorry if the format isn't perfect or if i attached the code snippets in a inconvenient way, please give me some pointers if thats the case
Since there is no two-way binding in react, the normal way of processing form input with controlled components is to pull the text from the specific dom element every time it changes. We do this using event.target.value to pull the data, but how do we then add it correctly to your state in a reusable component?
For that we would want to add a name tag to the input field, which we would then use when adding the value to our state. As you correctly stated, formData.someName = 'some text' We update our state in a changeHandler function that updates our state every time the input changes.
For example, assume we are passing setFormData, formData and a fieldName into the props:
export default function InputField(props){
const {setFormData, formData, fieldName} = props //destructure the props
const changeHandler = (event) => {
// updates the formData state of formData.name to equal event.target.value
setFormData({...formData, [event.target.name]: event.target.value})
}
return(
<TextField
fullWidth
name={fieldName}
value={formdata.fieldName}
onChange={changeHandler}
variant='standard'
/>
)
}
Using the above component would look something like this:
<InputField setFormData={setFormData} formData={formData} fieldName="question1">
I was able to get it working by passing the property into the component that needed to be changed; here is my handleChange function on the textfield component:
const handleChange = (event) => {
var formData = {...props.formData}
formData[props.property] = event.target.formData
props.onChange(formData)
}

What is the relationship between onChange and value when using react?

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.

How to make a properly controlled and reactive component with JSX in Vue

I have an input component that is largely just a presentation wrapper for the actual input field itself. The parent component should have control of the value and validation and the like.
Below is a simple sketch of the code and the two components:
TextInput.js
export default {
name: "TextInput",
props: {
value: [String, Number]
},
methods: {
inputted(e) {
this.$emit('input', e);
}
},
render() {
return (
<input
value={this.value}
onInput={(e) => this.inputted(e)}
/>
);
}
}
Example.js
export default {
data() {
return {
depositAmount: ""
};
},
methods: {
handleInput(newValue) {
let parts = newValue.match(/^(\d+)?(\.)?(\d{0,6})?$/);
if (parts !== null) {
this.depositAmount = parts[0];
}
},
},
render() {
return (
<TextInput
value={this.depositAmount}
onInput={(e) => this.handleInput(e.target.value)}
/>
);
}
}
It would seem to me that the lifecycle here should be roughly the following:
user enters a string
onInput fires on the base <input> tag
onInput fires on TextInput
handleInput is called and validates the full string against the regex
if the new input is valid, depositAmount is updated, if not nothing happens
the value prop on TextInput is set to depositAmount
the value of the base <input> tag is set to value from TextInput (which is depositAmount)
The docs make it seem as though this should be the case.
This is all fine and dandy when the input is valid. However, when it is not valid, depositAmount does not change but the value of the base <input> element does change.
How can I make the value of the input 1:1 with the parent's data, even when the input updates but the parent's data doesn't?
Working example. As you can see, the <input> that is disabled and simply taking the value of the validated and sanitized input via depositAmount works properly. But the <input> that is being interacted with and typed into ignores its controlled value and simply reflects what the user has entered.
PSEUDO-SOLUTION
The only way I've found to solve this is to add this.$forceUpdate() inside the root event handler
TextInput.js
...
methods: {
inputted(e) {
this.$emit('input', e);
this.$forceUpdate();
}
}
...
I say this is a "pseudo" solution because while it does solve the problem and forces the input to always take the value it's being fed from its parent, I'm left unsatisfied as I deplore using $forceUpdate when it feels like this should be a situation where "it just works."
So while there is a quick patch, I'm leaving this open as I'd love to hear an actual solution or hear what I'm not grasping here that is necessitating the $forceUpdate. Seems like Vue's diffing should notice that the prop is different from the actual value of the <input> element even though they should be bound.

How do I get inputRef to return <input> element from <Textfield>? Tried everything I can think of and it's still null

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

Highlighting value change ReactJS

I am using ReactJS, and I was wondering whether it is possible to highlight an element in the DOM when its value has been changed.
I have a list of elements whose values update periodically. While I have no problem animating in the DOM new items coming in to the list or items leaving the list using React's animation library interface, I am struggling to figure out how to detect the actual value change of the existing elements in the list.
Any ideas on how to do so? Thank you!
I had the same problem, then I decided to create a generic small module to handle it everywhere, that you can install from here
https://www.npmjs.com/package/react-change-highlight
using
yarn add react-change-highlight
what you can do to use it is to wrap you part that you want to highlight inside the component itself as follow
import ChangeHighlight from 'react-change-highlight';
export default () => {
const [count, setCount] = useState(0);
return (
<ChangeHighlight>
<div ref={React.createRef()}>{count}</div>
</ChangeHighlight>
);
}
and you will find the highlight on change as in the example below
I hope you find this useful
If your new values are coming down as new props, then you can use the componentWillReceiveProps lifecycle method on the component. It is called just before the component is updated.
Within that method, you can compare the new values (passed as the argument to the method) and the old values (available as this.props).
For instance:
componentWillReceiveProps: function(newProps) {
if(this.props.query != newProps.query) {
//handle change of query prop
//may include calls to this.setState()
}
}
Assuming that your elements are input elements, you need to use the onChange event listener. If you want to highlight that element, I would suggest having a CSS class that highlights the element and then conditionally applying it in render() based on state (or props if a child).
So for instance, you can have one component, add a handler function and the appropriate listener in the render():
handleChange: function(evt) {
//input was changed, update component state (or call parent method if child)
this.setState({changed: true});
},
render: function() {
return (<div>
<input type="text"
className={this.state.changed ? 'highlight' : ''}
onChange={this.handleChange}
/>
</div>);
}
That should set you in the right direction.

Categories