React: trigger onChange if input value is changing by state? - javascript

Edit: I don't want to call handleChange only if the button has been clicked. It has nothing to do with handleClick. I gave an example in the #shubhakhatri answer's comment.
I want to change the input value according to state, the value is changing but it doesn't trigger handleChange() method. How can I trigger handleChange() method ?
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
value: 'random text'
}
}
handleChange (e) {
console.log('handle change called')
}
handleClick () {
this.setState({value: 'another random text'})
}
render () {
return (
<div>
<input value={this.state.value} onChange={this.handleChange}/>
<button onClick={this.handleClick.bind(this)}>Change Input</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
Here is the codepen link: http://codepen.io/madhurgarg71/pen/qrbLjp

You need to trigger the onChange event manually. On text inputs onChange listens for input events.
So in you handleClick function you need to trigger event like
handleClick () {
this.setState({value: 'another random text'})
var event = new Event('input', { bubbles: true });
this.myinput.dispatchEvent(event);
}
Complete code
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
value: 'random text'
}
}
handleChange (e) {
console.log('handle change called')
}
handleClick () {
this.setState({value: 'another random text'})
var event = new Event('input', { bubbles: true });
this.myinput.dispatchEvent(event);
}
render () {
return (
<div>
<input readOnly value={this.state.value} onChange={(e) => {this.handleChange(e)}} ref={(input)=> this.myinput = input}/>
<button onClick={this.handleClick.bind(this)}>Change Input</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
Codepen
Edit:
As Suggested by #Samuel in the comments, a simpler way would be to call handleChange from handleClick if you don't need to the event object in handleChange like
handleClick () {
this.setState({value: 'another random text'})
this.handleChange();
}
I hope this is what you need and it helps you.

I tried the other solutions and nothing worked. This is because of input logic in React.js has been changed. For detail, you can see this link: https://hustle.bizongo.in/simulate-react-on-change-on-controlled-components-baa336920e04.
In short, when we change the value of input by changing state and then dispatch a change event then React will register both the setState and the event and consider it a duplicate event and swallow it.
The solution is to call native value setter on input (See setNativeValue function in following code)
Example Code
import React, { Component } from 'react'
export class CustomInput extends Component {
inputElement = null;
// THIS FUNCTION CALLS NATIVE VALUE SETTER
setNativeValue(element, value) {
const valueSetter = Object.getOwnPropertyDescriptor(element, 'value').set;
const prototype = Object.getPrototypeOf(element);
const prototypeValueSetter = Object.getOwnPropertyDescriptor(prototype, 'value').set;
if (valueSetter && valueSetter !== prototypeValueSetter) {
prototypeValueSetter.call(element, value);
} else {
valueSetter.call(element, value);
}
}
constructor(props) {
super(props);
this.state = {
inputValue: this.props.value,
};
}
addToInput = (valueToAdd) => {
this.setNativeValue(this.inputElement, +this.state.inputValue + +valueToAdd);
this.inputElement.dispatchEvent(new Event('input', { bubbles: true }));
};
handleChange = e => {
console.log(e);
this.setState({ inputValue: e.target.value });
this.props.onChange(e);
};
render() {
return (
<div>
<button type="button" onClick={() => this.addToInput(-1)}>-</button>
<input
readOnly
ref={input => { this.inputElement = input }}
name={this.props.name}
value={this.state.inputValue}
onChange={this.handleChange}></input>
<button type="button" onClick={() => this.addToInput(+1)}>+</button>
</div>
)
}
}
export default CustomInput
Result

I think you should change that like so:
<input value={this.state.value} onChange={(e) => {this.handleChange(e)}}/>
That is in principle the same as onClick={this.handleClick.bind(this)} as you did on the button.
So if you want to call handleChange() when the button is clicked, than:
<button onClick={this.handleChange.bind(this)}>Change Input</button>
or
handleClick () {
this.setState({value: 'another random text'});
this.handleChange();
}

In a functional component you can do this, let's assume we have a input[type=number]
const MyInputComponent = () => {
const [numberValue, setNumberValue] = useState(0);
const numberInput = useRef(null);
/**
* Dispatch Event on Real DOM on Change
*/
useEffect(() => {
numberInput.current.dispatchEvent(
new Event("change", {
detail: {
newValue: numberValue,
},
bubbles: true,
cancelable: true,
})
);
}, [numberValue]);
return (
<>
<input
type="number"
value={numberValue}
ref={numberInput}
inputMode="numeric"
onChange={(e) => setNumberValue(e.target.value)}
/>
</>
)
}

The other answers talked about direct binding in render hence I want to add few points regarding that.
You are not recommended to bind the function directly in render or anywhere else in the component except in constructor. Because for every function binding a new function/object will be created in webpack bundle js file hence the bundle size will grow. Your component will re-render for many reasons like when you do setState, new props received, when you do this.forceUpdate() etc. So if you directly bind your function in render it will always create a new function. Instead do function binding always in constructor and call the reference wherever required. In this way it creates new function only once because constructor gets called only once per component.
How you should do is something like below
constructor(props){
super(props);
this.state = {
value: 'random text'
}
this.handleChange = this.handleChange.bind(this);
}
handleChange (e) {
console.log('handle change called');
this.setState({value: e.target.value});
}
<input value={this.state.value} onChange={this.handleChange}/>
You can also use arrow functions but arrow functions also does create new function every time the component re-renders in certain cases. You should know about when to use arrow function and when are not suppose to. For detailed explation about when to use arrow functions check the accepted answer here

you must do 4 following step :
create event
var event = new Event("change",{
detail: {
oldValue:yourValueVariable,
newValue:!yourValueVariable
},
bubbles: true,
cancelable: true
});
event.simulated = true;
let tracker = this.yourComponentDomRef._valueTracker;
if (tracker) {
tracker.setValue(!yourValueVariable);
}
bind value to component dom
this.yourComponentDomRef.value = !yourValueVariable;
bind element onchange to react onChange function
this.yourComponentDomRef.onchange = (e)=>this.props.onChange(e);
dispatch event
this.yourComponentDomRef.dispatchEvent(event);
in above code yourComponentDomRef refer to master dom of your React component for example <div className="component-root-dom" ref={(dom)=>{this.yourComponentDomRef= dom}}>

Approach with React Native and Hooks:
You can wrap the TextInput into a new one that watches if the value changed and trigger the onChange function if it does.
import React, { useState, useEffect } from 'react';
import { View, TextInput as RNTextInput, Button } from 'react-native';
// New TextInput that triggers onChange when value changes.
// You can add more TextInput methods as props to it.
const TextInput = ({ onChange, value, placeholder }) => {
// When value changes, you can do whatever you want or just to trigger the onChange function
useEffect(() => {
onChange(value);
}, [value]);
return (
<RNTextInput
onChange={onChange}
value={value}
placeholder={placeholder}
/>
);
};
const Main = () => {
const [myValue, setMyValue] = useState('');
const handleChange = (value) => {
setMyValue(value);
console.log("Handling value");
};
const randomLetters = [...Array(15)].map(() => Math.random().toString(36)[2]).join('');
return (
<View>
<TextInput
placeholder="Write something here"
onChange={handleChange}
value={myValue}
/>
<Button
title='Change value with state'
onPress={() => setMyValue(randomLetters)}
/>
</View>
);
};
export default Main;

I know what you mean, you want to trigger handleChange by click button.
But modify state value will not trigger onChange event, because onChange event is a form element event.

I had a similar need and end up using componentDidMount(), that one is called long after component class constructor (where you can initialize state from props - as an exmple using redux )
Inside componentDidMount you can then invoke your handleChange method for some UI animation or perform any kind of component properties updates required.
As an example I had an issue updating an input checkbox type programatically, that's why I end up using this code, as onChange handler was not firing at component load:
componentDidMount() {
// Update checked
const checkbox = document.querySelector('[type="checkbox"]');
if (checkbox)
checkbox.checked = this.state.isChecked;
}
State was first updated in component class constructor and then utilized to update some input component behavior

Try this code if state object has sub objects like this.state.class.fee. We can pass values using following code:
this.setState({ class: Object.assign({}, this.state.class, { [element]: value }) }

Related

Can variable be passing by function (react js)?

I wrote a class component and mutilple functions in it(class) , but don't know how variable be passing between different function.
class App extends Component {
state = {
stringA:null,
stringB:null
};
set_A = (event) =>{
const stringA = 'text';
}
copy_A = (event) =>{
const stringB = stringA;
}
render() {
return (
<>
<button onClick={this.set_A} type="click">set</button>
<button onClick={this.copy_A} type="click">copy</button>
</>
);}
}
export default App;
I reference this docs , but it only said function component without class component.
https://www.robinwieruch.de/react-function-component
or, are state and props not a kind of variable?
You access your properties with this.props and your state with this.state. You change state by calling setState which accepts partial states and merges them into the full state. It also triggers a re-render so that state changes can be seen in the UI.
class App extends Component {
state = {
stringA:null,
stringB:null
};
set_A = (event) => {
this.setState({ stringA: 'text' });
}
copy_A = (event) => {
this.setState({ stringB: this.state.stringA });
}
render() {
return (
<>
<button onClick={this.set_A} type="click">set</button>
<button onClick={this.copy_A} type="click">copy</button>
</>
);
}
}
export default App;
So in React, you would not be assigning a value to a variable like that. You would be utilizing State functionality. For Class-based React you would be using this.setState({stringA: 'text'})
or
this.setState({stringB: stringA})
Once the values are in the state you can access them anywhere in the component from the state object this.state.stringB for instance would have the value that was set once you had clicked on copy button
Example
set_A = (event) => {
this.setState({ stringA: 'text' });
console.log(this.state.stringB)
}
copy_A = (event) => {
this.setState({ stringB: this.state.stringA });
}
React Documentation is also a great resource to reference for Class and Function based component behaviors. https://reactjs.org/docs/state-and-lifecycle.html#adding-local-state-to-a-class
You change state by calling setState in class based components
Try this :
class App extends Component {
state = { stringA:null, stringB:null };
set_A = (e) => {
this.setState({...state, stringA: 'text' });
}
copy_A = (e) => {
this.setState({ ...state,stringB: this.state.stringA });
}
render() {
return (
<>
<button onClick={this.set_A} type="click">set</button>
<button onClick={this.copy_A} type="click">copy</button>
</>
);
}
}
export default App;
Use the spread operator {...state} to change only the targeted piece of state you want to change with no change in the other pieces of the state.

Is there a way to pass in a variable dynamically into a toggle function without causing a looping error

I am trying to create a dynamic handler function that allows me to choose which state it is toggling true or false and pass this into a component
So far I get maximum depth exceeded
class Demo extends React.Component {
state = {
state1: false,
state2: false,
}
handleChange = (input) => {
this.setState(prevState => ({[input]: !prevState.input}));
};
render() {
const { state1 } = this.state
return (
<DemoComp2 handleChange={this.handleChange} state1={state1}/>
)
}
}
Next component
class DemoComp2 extends React.Component {
render() {
const { state1, handleChange } = this.props;
return(
<button onClick={handleChange('state1')}>test</button>
{ state1 === true ? <p> true </p> : <p> false </p> }
)
}
}
Because in DemoComp2 onClick is calling handleChange() and not handleChange it infinitely loops.
But I need to pass state1 to the function with handleChange('state1') so it knows which state to toggle.
Is there another way of doing this that is as concise or do I need to break it out into separate functions?
I see two problems here.
Your initial state is:
{
state1: false,
state2: false,
}
But when you update it, you're changing the format. You should use the spread operator to get the other parts of prevState:
handleChange = input => {
this.setState(prevState => {
return {
...prevState,
[input]: !prevState[input],
}
})
}
Secondly, handleChange isn't receiving the state1 value - it's receiving the JS event. You need to call a function that calls handleChange:
<button onClick={() => handleChange('state1')}>test</button>
What is happen here is that your onClick func is being called on each render instead only on onClick event. Just pass it an arrow func to avoid it.
onClick={() => handleChange(‘state1’)}
First of all, I do not think the code you shared works.
{ state === true ? <p> true </p> : <p> false </p> }
you have not declared state anywhere on Demo2 component
Second of all, you are executing the handleChange function right the way, instead of executing it when the button is clicked.
Solution
class DemoComp2 extends React.Component {
render() {
const { state1, handleChange } = this.props;
return(
<button onClick={() => handleChange('state1')}>test</button>
{ state1 === true ? <p> true </p> : <p> false </p> }
)
}
}

How to remove ReactJS warning related to using a `value` prop to a form field without an `onChange` handler when the handler is actually defined?

So have an input field:
<input
id={itemId}
onChange={handleChange}
type="number"
value={packed}
/>
And here is my onChange function:
handleChange(e) {
const { items, onUpdateQuantity } = this.props;
const updateItem = items.filter((item) =>
item.itemId === e.target.id,
);
const itemQuantity = toNumber(e.target.value);
updateItem.total += itemQuantity;
onUpdateQuantity(e.target.id, itemQuantity);
}
So why is React still complaining about an onChange handler not being defined when it already is? I don't want to add a defaultValue prop, as that causes bugs in my app. Any ideas?
That is coming because your value is not changing anywhere. As you can see from docs for controlled components, the value of the input is this.state.value and the onChange method changes the value inside the input by changing this.state.value.
As far as I can see, when you input a value inside the input (<input/>) element, the value of that element is not changing. It is always whatever the value of packed is. That is why you're getting the error.
Make sure you bind you function in the constructor
class bla extends Component {
constructor(props){
super(props);
this.handleChange = this.handleChange.bind(this);
}
}
or if using stage 0 to have you have your function in this way.
handleChange = () => {}
and if not
handleChange () {
return (e) => {};
}
Also, if your using a class component you should call your handleChange with this.handleChange if your passing it to functional component then what you have should be fine
https://reactjs.org/docs/handling-events.html
You have to be careful about the meaning of this in JSX callbacks. In JavaScript, class methods are not bound by default. If you forget to bind this.handleClick and pass it to onClick, this will be undefined when the function is actually called.
This is not React-specific behavior; it is a part of how functions work in JavaScript. Generally, if you refer to a method without () after it, such as onClick={this.handleClick}, you should bind that method.
Don't forget to use this.handleChange in you JSX
Example:
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);

Handler not working as expected in child component in React

I am on the last spec in a section of a test from school.
select should have an onChange event that submits the new animal
Essentially I can't seem to get the handleSubmit function passed into the child, to respond to the changes...
This is the rest of the spec.
it('select should have an onChange event that submits the new animal', () => {
expect(animalSelect.props('select').onChange).to.be.function;
// choosing a random animal
let animal = getRandomAnimal();
// simulating a 'change' event with an event described as the second argument given to `simulate`
animalSelect.find('select').simulate('change', { target: { value: animal } });
// the spy sent in should be called with the argument described
expect(setAnimalSpy.calledWith(animal)).to.be.true;
});
This is the parent component Exhibit:
import React, { Component } from 'react';
import AnimalSelect from './AnimalSelect';
import Cage from './Cage';
export default class Exhibit extends Component {
constructor(props) {
super(props);
this.state = {
selectedAnimal: this.props.selectedAnimal,
};
this.setAnimal = this.setAnimal.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
setAnimal(animal) {
this.setState({ selectedAnimal: animal });
}
handleSubmit(event) {
this.setState({ selectedAnimal: event.target.value });
}
render() {
const { selectedAnimal } = this.state;
return (
<div className="exhibit">
<AnimalSelect handleSubmit={this.handleSubmit} submitAnimal={this.setAnimal} animals={this.props.animals} />
<Cage selectedAnimal={selectedAnimal} />
</div>
);
}
}
This is the AnimalSelect (The child of Exhibit) component:
import React, { Component } from 'react';
// exporting the constructor function (dumb component).
// what is the parameter coming in here?
export default function AnimalSelect(props) {
// prettier-ignore
return (
<form>
<label>Select an Animal: </label>
<select onChange={() => props.handleSubmit}>
{props.animals.map((animal, index) => {
return (
<option key={animal} value={animal}>
{animal}
</option>
);
})}
</select>;
</form>
);
}
Unfortunately this is the only error I am getting.
AssertionError: expected false to be true
Any ideas?
Here, you set the event handler to be an anonymous function which returns a reference to a function:
<select onChange={() => props.handleSubmit}>
You probably intend something more like this:
<select onChange={evt => props.handleSubmit(evt)}>
This effectively delegates the event handler to the parent component's function, passing the event object along to it. Although I'm uncertain why setting the handler as suggested in the comment didn't work.

Notify react components about value change

Suppose that I have a component class which is responsible to change any number entered into textbox to text:
class NumbersToText extends Component {
onChange(event) {
const { target } = event;
const { value } = target;
if (hasNumbers(value)) {
target.value = numbersToText(value);
// HERE I NEED TO NOTIFY ABOUT CHANGES
}
}
render() {
return (
<span onChange={this.onChange}>
{this.props.children}
</span>
);
}
}
Now the usage would look something like this:
<NumbersToText>
<input onChange={this.saveValue}
</NumbersToText>
Let's say that all works, and the value gets changed to text.
Now the problem is that after I change numbers to text and assign that value to input onChange handlers are not executed again, thus saveValue is not called with updated value.
How should this problem be approached in order to trigger onChange handlers with new value?
I don't know exactly what you mean by numbers to text so I'll just assume you want to modify the value before calling the onChange function in the input, and also reflect that value in the input.
First of all, what you're doing will never work on React, React reflects internal virtual objects into the DOM, meaning you shloud not modify the DOM directly and instead you should modify this internal representantion (via setState, props) to reflect this change into the DOM.
There's also two types of inputs on React, controlled and uncontrolled. I will assume you want to use this on uncontrolled inputs.
The only possible solution I can see, is to transform the input using the React.cloneElement function adding a aditional step before calling the input's onChange callback.
Here's a possible implementation that will make the input uppercase.
class UpperCase extends React.Component {
constructor(props) {
super(props);
}
onChange(e, input, next) {
let value = input.value || '';
value = value.toUpperCase();
input.value = value;
next(value);
}
render() {
let childs = React.Children.map(this.props.children, child => {
let input = null; //Will take advantage of javascript's closures
let onChangeChild = child.props.onChange.bind(child);
return React.cloneElement(child, {
ref: ref => input = ref,
onChange: e => {
this.onChange(e, input, onChangeChild)
}
});
});
return (
<span>
{childs}
</span>
);
}
}
And you can use it like this:
<UpperCase>
<input onChange={(val) => console.log(val)}></input>
<textarea onChange={(val) => console.log(val)}></textarea>
</UpperCase>
Thanks to #tiagohngl I came up with a similar, but maybe a little less cluttered (without cloning elements) way:
class NumbersToText extends Component {
onChange(event) {
const { target } = event;
const { value } = target;
if (hasNumbers(value)) {
target.value = numbersToText(value);
this.childrenOnChange(event);
}
}
childrenOnChange(event) {
const { children } = this.props;
React.Children.forEach(children, child => {
if (child.props.onChange) {
child.props.onChange(event);
}
});
}
render() {
return (
<span onChange={this.onChange}>
{this.props.children}
</span>
);
}
}
export default class NumbersToText extends React.Component {
constructor(props) {
super(props)
this.onChange = this.onChange.bind(this);
}
componentWillMount() {
this.setState({ anyData: [] });
}
onChange(event) {
this.setState({anyData: event.target.value},
()=>{console.log("AnyData: "+this.state.anyData)});
// callback to console.log after setState is done
}
render() {
return (
<input type="text"
value={this.state.anyData}
onChange={this.onChange} />
);
}
}
As you mention that,
onChange is not called after changed value.
There are multiple possibilities.
onChange is not binded.
There are no state change in render method, so it will not re-render
make use of console.log() to trace the problem
I slightly ammend the code for illustration.
Hope it helps.
How react handle State Change (answer I posted before)

Categories

Resources