If I create a component like this:
const MyInput = ({username})=>{
return (
<div>
<input type="text" value={username}/>
</div>
);
};
nothing will happen when I type in the input, because react needs to call setState() to update it.
so what I do is add a onChange method to handle it:
handleOnchange(e){
e.preventDefault();
let name=e.target.value;
this.setState({username:name});
}
But I think having to handle every onChange manually is too much boilerplate, Is this a better way to save the value? can it be done using ref?
Fiddle Link
Yes, you need to call setState in order for the value to update on the input.
See here for a good example: https://facebook.github.io/react/docs/forms.html
Copied here:
getInitialState: function() {
return {value: 'Hello!'};
},
handleChange: function(event) {
this.setState({value: event.target.value});
},
render: function() {
return (
<input
type="text"
value={this.state.value}
onChange={this.handleChange}
/>
);
}
There is a solution to this issue that is common in javascript and implemented by lodash. Take a look at using debouncing to wait for the user to stop entering text before trying to update. Here is an analogy of how it works from someone more eloquent than me:
Think of it as "grouping multiple events in one". Imagine that you go
home, enter in the elevator, doors are closing... and suddenly your
neighbor appears in the hall and tries to jump on the elevator. Be
polite! and open the doors for him: you are debouncing the elevator
departure. Consider that the same situation can happen again with a
third person, and so on... probably delaying the departure several
minutes.
To do this in react you would import lodash and set onChange to be _.debounce(function(), delay) where function() would setState after the given delay.
This demo does a great job demoing how debounce works in react (just open the console and watch it as you type).
If you want to learn about best practices with debounced functions read the answers in this SO post.
Ps: you can also try throttling, though I think debouncing will be more desirable for your specific use case. The post with the analogy on debouncing also has one on throttling. To use it you would replace _.debounce(function, int) with _.throttle(function, int) in the examples
You can value link in react to implement 2 way data binding
class Something extends React.Component {
state = {
email: ''
}
emailValueLink = (value) => {
return {
value: this.state.email,
requestChange: this.handleEmailChange
};
}
handleEmailChange(newEmail) {
this.setState({email: newEmail});
}
render() {
<input type="email" valueLink={this.emailValueLink()} />
}
}
You can generalise emailValuelink by passing it in the key of the state to bind
React is all about splitting your code into components. So to prevent retyping this boilerplate code, you can create a component and reuse it for all form elements.
Or, like others have said, you can take somebody else's form or databinding components and reuse those.
Related
I would like to create a state object that combines both the state value itself and the ability to call setState whenever its value is changed. This way, e.g. on the interface between the container and the dumb component I won't have to pass a separate setter for each state property.
E.g. typically what gets done in the 'smart and dumb' pattern is the following:
let TextValueContainer = React.createClass({
getInitialState: function () {
return {value: ''};
},
setValue(v) {
this.setState({value: v});
},
render: function() {
return (
<TextValue
value={this.state.value}
setValue={this.setValue}
/>
);
}
});
let TextValue = React.createClass({
propTypes: {
value: React.PropTypes.string.isRequired,
setValue: React.PropTypes.func.isRequired
},
render: function() {
return (
<input type={'text'}
onChange={function (ev) {
this.props.setValue(ev.target.value);
}.bind(this)}
value={this.props.value}
>
</input>
);
}
});
ReactDOM.render(<TextValueContainer/>, $('#app')[0]);
Observe how at the interface between the container and the dumb component two properties are passed: the state value itself (value) and a method to change it (setValue). For N separate state attributes I would need 2*N props to be passed at the interface. Plus, there is no hard way looking at the code of the dumb component to figure out which setter is for which value.
I experimented a bit and come up with the following:
class StateHolder {
constructor(v, that) {
this.v = v;
this.setState = function(v2) {
this.setState(Object.assign({}
,this.state
,{valueHolder: new StateHolder(v2, that)}));
}.bind(that);
}
}
let TextValueContainer = React.createClass({
getInitialState: function () {
return {valueHolder: new StateHolder('', this)};
},
render: function() {
return (
<TextValue
valueHolder={this.state.valueHolder}
/>
);
}
});
let TextValue = React.createClass({
propTypes: {
valueHolder: React.PropTypes.instanceOf(StateHolder).isRequired
},
render: function() {
return (
<input type={'text'}
onChange={function (ev) {
this.props.valueHolder.setState(ev.target.value);
}.bind(this)}
value={this.props.valueHolder.v}
>
</input>
);
}
});
ReactDOM.render(<TextValueContainer/>, $('#app')[0]);
In the above implementation for each attribute, only a single props needs to be passed between the 'smart' and the 'dumb' component (in the example above valueHolder).
My questions are:
is there a simpler way to accomplish what I am trying to do? (i.e. simplify the interface between the 'smart' and the 'dumb' component and make explicit the association between the values passed down and their setter functions)
do you see any anti-patterns or code smells in the "solution" given above?
Looks like you'd want to take a look at some framework to manage your flow of data and the corresponding changes in the app state. I've been working with Redux a lot recently and I would recommend it. It's a very smart implementation of the Flux architecture. Your "stateHolder" concept is resolved in the Redux store.
From the Redux motivation page
Following in the steps of Flux, CQRS, and Event Sourcing, Redux
attempts to make state mutations predictable by imposing certain
restrictions on how and when updates can happen. These restrictions
are reflected in the three principles of Redux.
Of course, there are alternatives to Redux; the point is that what you're trying to do would become really hard to maintain and understand and that you should look at a generalised way to manage your state.
Tell me what do you think about this JSBin: http://jsbin.com/tofepoliha/edit?js,output
First, when declaring the TextValue element, instead of using value={this.state.value} value2={this.state.value2}, we pass the whole state in one action using the spread (...) operator.
<TextValue {...this.state} setValue={this.setValue} />
This way we don't need to repeat ourselves for each and every property. Now, for the setValue function - instead of having a special function for every property, we can simply declare one generic function, that gets the key and the value and sets it to the parent component's state:
setValue(value, v) {
this.setState({[value]: v});
}
Then, we can have as many inputs (or every other element for this matter) like so:
<input type="text" onChange={function (ev) {
this.props.setValue('value', ev.target.value);
}.bind(this)} value={this.props.value} />
<input type="text" onChange={function (ev) {
this.props.setValue('value2', ev.target.value);
}.bind(this)} value={this.props.value2} />
I have been trying to figure out the best way to manage my react forms. I have tried to use the onChange to fire an action and update my redux store with my form data. I have also tried creating local state and when my form gets submitted I trigger and action and update the redux store.
How should i manage my controlled input state?
I like this answer from one of the Redux co-authors:
https://github.com/reactjs/redux/issues/1287
Use React for ephemeral state that doesn't matter to the app globally
and doesn't mutate in complex ways. For example, a toggle in some UI
element, a form input state. Use Redux for state that matters globally
or is mutated in complex ways. For example, cached users, or a post
draft.
Sometimes you'll want to move from Redux state to React state (when
storing something in Redux gets awkward) or the other way around (when
more components need to have access to some state that used to be
local).
The rule of thumb is: do whatever is less awkward.
That is, if you're sure that your form won't affect global state or need to be kept after your component is unmounted, then keep in the react state.
You can use the component's own state. And then take that state and give it as an argument to the action. That's pretty much the "React way" as described in the React Docs.
You can also check out Redux Form. It does basically what you described and links the form inputs with Redux State.
The first way basically implies that you're doing everything manually - maximum control and maximum boilerplate. The second way means that you're letting the higher order component do all the work for you. And then there is everything in between. There are multiple packages that I've seen that simplify a specific aspect of form management:
React Forms -
It provides a bunch of helper components to make form rendering and validation more simple.
React JSON schema -
Allows one to build an HTML form from a JSON schema.
Formsy React -
As the description says: "This extension to React JS aims to be that "sweet spot" between flexibility and reusability."
Update: seems these days Redux Form is being replaced with:
React Final Form
And one more important contender in the space that's worth checking out is:
Formik
Tried out React Hook Form in my last project - very simple, small footprint and just works:
React Hook Form
TL;DR
It's fine to use whatever as it seems fit to your app (Source: Redux docs)
Some common rules of thumb for determing what kind of data should be
put into Redux:
Do other parts of the application care about this data?
Do you need to be able to create further derived data based on this original data?
Is the same data being used to drive multiple components?
Is there value to you in being able to restore this state to a given point in time (ie, time travel debugging)?
Do you want to cache the data (ie, use what's in state if it's already there instead of re-requesting it)?
These questions can easily help you identify the approach that would be a better fit for your app. Here are my views and approaches I use in my apps (for forms):
Local state
Useful when my form has no relation to other components of the UI. Just capture data from input(s) and submits. I use this most of the time for simple forms.
I don't see much use case in time-travel debugging the input flow of my form (unless some other UI component is dependent on this).
Redux state
Useful when the form has to update some other UI component in my app (much like two-way binding).
I use this when my form input(s) causes some other components to render depending on what is being input by the user.
Personally, I highly recommend keeping everything in the Redux state and going away from local component state. This is essentially because if you start looking at ui as a function of state you can do complete browserless testing and you can take advantage of keeping a reference of full state history (as in, what was in their inputs, what dialogs were open, etc, when a bug hit - not what was their state from the beginning of time) for the user for debugging purposes. Related tweet from the realm of clojure
edited to add: this is where we and our sister company are moving in terms of our production applications and how we handle redux/state/ui
Using the helper libraries is just more quick and avoid us all the boilerplate. They may be optimized, feature rich ...etc. As they make all different aspects more of a breeze. Testing them and making your arsenal as knowing what's useful and better for the different needs, is just the thing to do.
But if you already implemented everything yourself. Going the controlled way. And for a reason you need redux. In one of my projects. I needed to maintain the form states. So if i go to another page and come back it will stay in the same state. You only need redux, if it's a mean for communicating the change to multiple components. Or if it's a mean to store the state, that you need to restore.
You need redux, if the state need to be global. Otherwise you don't need it. For that matter you can check this great article here for a deep dive.
One of the problems that you may encounter! When using the controlled inputs. Is that you may just dispatch the changements at every keystroke. And your form will just start freezing. It became a snail.
You should never directly dispatch and use the redux flux, at every changement.
What you can do, is to have the inputs state stored on the component local state. And update using setState(). Once the state change, you set a timer with a delay. It get canceled at every keystroke. the last keystroke will be followed by the dispatching action after the specified delay. (a good delay may be 500ms).
(Know that setState, by default handle the multiple successive keystroke effectively. Otherwise we would have used the same technique as mentioned above (as we do in vanilla js) [but here we will rely on setState])
Here an example bellow:
onInputsChange(change, evt) {
const { onInputsChange, roomId } = this.props;
this.setState({
...this.state,
inputs: {
...this.state.inputs,
...change
}
}, () => {
// here how you implement the delay timer
clearTimeout(this.onInputsChangeTimeoutHandler); // we clear at ever keystroke
// this handler is declared in the constructor
this.onInputsChangeTimeoutHandler = setTimeout(() => {
// this will be executed only after the last keystroke (following the delay)
if (typeof onInputsChange === "function")
onInputsChange(this.state.inputs, roomId, evt);
}, 500);
})
}
You can use the anti-pattern for initializing the component using the props as follow:
constructor(props) {
super(props);
const {
name,
description
} = this.props;
this.state = {
inputs: {
name,
description
}
}
In the constructor or in the componentDidMount hook like bellow:
componentDidMount () {
const {
name,
description
} = this.props;
this.setState({
...this.state,
inputs: {
name,
description
}
});
}
The later allow us to restore the state from the store, at every component mounting.
Also if you need to change the form from a parent component, you can expose a function to that parent. By setting for setInputs() method that is binded. And in the construction, you execute the props (that is a getter method) getSetInputs(). (A useful case is when you want to reset the forms at some conditions or states).
constructor(props) {
super(props);
const {
getSetInputs
} = this.props;
// .....
if (typeof getSetInputs === 'function') getSetInputs(this.setInputs);
}
To understand better what i did above, here how i'm updating the inputs:
// inputs change handlers
onNameChange(evt) {
const { value } = evt.target;
this.onInputsChange(
{
name: value
},
evt
);
}
onDescriptionChange(evt) {
const { value } = evt.target;
this.onInputsChange(
{
description: value
},
evt
);
}
/**
* change = {
* name: value
* }
*/
onInputsChange(change, evt) {
const { onInputsChange, roomId } = this.props;
this.setState({
...this.state,
inputs: {
...this.state.inputs,
...change
}
}, () => {
clearTimeout(this.onInputsChangeTimeoutHandler);
this.onInputsChangeTimeoutHandler = setTimeout(() => {
if (typeof onInputsChange === "function")
onInputsChange(change, roomId, evt);
}, 500);
})
}
and here is my form:
const {
name='',
description=''
} = this.state.inputs;
// ....
<Form className="form">
<Row form>
<Col md={6}>
<FormGroup>
<Label>{t("Name")}</Label>
<Input
type="text"
value={name}
disabled={state === "view"}
onChange={this.onNameChange}
/>
{state !== "view" && (
<Fragment>
<FormFeedback
invalid={
errors.name
? "true"
: "false"
}
>
{errors.name !== true
? errors.name
: t(
"You need to enter a no existing name"
)}
</FormFeedback>
<FormText>
{t(
"Enter a unique name"
)}
</FormText>
</Fragment>
)}
</FormGroup>
</Col>
{/* <Col md={6}>
<div className="image">Image go here (one day)</div>
</Col> */}
</Row>
<FormGroup>
<Label>{t("Description")}</Label>
<Input
type="textarea"
value={description}
disabled={state === "view"}
onChange={this.onDescriptionChange}
/>
{state !== "view" && (
<FormFeedback
invalid={
errors.description
? "true"
: "false"
}
>
{errors.description}
</FormFeedback>
)}
</FormGroup>
</Form>
In looking around to see what ways other developers are handling input focus when working with Redux I've come across some general guidance for ReactJS components such as this. My concern however is that the focus() function is imperative and I could see strange behaviours possible where multiple components are fighting over focus. Is there a redux way of dealing with focus? Is anybody dealing with pragmatically setting focus using redux and react and if so what techniques do you use?
Related:
How to set focus on an element in Elm?
Automatically focus input element after creation in purescript-halogen
https://github.com/cyclejs/cycle-core/issues/153
My approach is using ref callback, which is kind of an onRenderComplete of an element. In that callback I can focus (conditionally, if needed) and gain a reference for future focusing.
If the input is rendered conditionally after an action runs, that ref callback should fire a focus, because the ref doesn't exist yet immediately after calling the action, but only after render is done. Dealing with componentDidUpdate for things like focus just seems like a mess.
// Composer.jsx -- contains an input that will need to be focused somewhere else
class Composer extends Component {
render() {
return <input type="text" ref="input" />
}
// exposed as a public method
focus() {
this.refs.input.focus()
}
}
// App.jsx
#connect(
state => ({ isComposing: state.isComposing }),
...
)
class App extends Component {
render() {
const { isComposing } = this.props // or props, doesn't matter
return (
<div>
<button onClick={::this._onCompose}>Compose</button>
{isComposing ? <Composer ref={c => {
this._composer = c
this._composer && this._composer.focus() // issue initial focus
}} /> : null}
</div>
)
}
_onCompose() {
this.props.startComposing() // fire an action that changes state.isComposing
// the first time the action dispatches, this._composer is still null, so the ref takes care of the focus. After the render, the ref remains so it can be accessed:
this._composer && this._composer.focus() // focus if ref already exists
}
}
Why not autoFocus or isFocued prop?
As HTMLInputElement has value as a prop, but focus() as a method -- and not isFocused prop -- I would keep using methods to handle that. isFocused can get a value but if the user blurs from the input, what happens to that value? It'll be out of sync. Also, as mentioned in the comments, autoFocus can conflict with multiple components
So how to decide between props and methods?
For most cases props will be the answer. Methods can be used only in a 'fire and forget' things, such as scrollToBottom in a chat when a new message comes in, scrollIntoView and such. These are one time behaviors that the store doesn't care about and the user can change with an interaction, so a boolean prop won't fit. For all other things, I'd go with props.
Here's a jsbin:
http://jsbin.com/waholo/edit?html,js,output
This is not a question as much "how to make this work" as much as it is a "was this the best way." Here's my code:
/**
* React Static Boilerplate
* https://github.com/koistya/react-static-boilerplate
* Copyright (c) Konstantin Tarkus (#koistya) | MIT license
*/
import React, { Component } from 'react';
// import './InputWidgetText.scss';
import ContentBlock from '../ContentBlock';
var i = 0;
var contentBlocks = [];
var ContentContainer = React.createClass({
addNewBlock: function(){
i++;
contentBlocks.push(<ContentBlock key={i} index={i}/>)
this.forceUpdate();
},
render: function(){
if (this.props.inputs) {
contentBlocks = this.props.inputs.map(function(item, index){
i++;
return(<ContentBlock key={index} index={index} content={item} />)
});
}
return (
<div>
{contentBlocks}
<button onClick={this.addNewBlock}>+</button>
</div>
)
}
});
export {ContentContainer as default};
The problem is that every so often on a refresh the props.inputs are not getting passed down to this component and throwing an error when I try to map undefined. So the simple solution is to put the map process in an if check for whether or not the props are there yet - is that actually the right way to handle this? My data is passed in via a reflux mixin on the parent. I just feel like there might be a more proper way to handle this. Thanks for the feedback!
May I strongly suggest you refactor your code to do away with the file variables i and contentBlocks.
The contentBlocks variable seems completely unnecessary, whilst your i variable should be part of the state. Whilst you're at it, give i a more meaningful name, e.g. blockCount.
getInitialState: function () {
return {
blockCount: 0
};
},
Then define your click event handler to modify the state:
addNewBlock: function () {
this.setState({
blockCount: this.state.blockCount + 1
});
},
Every time you call setState(), React will trigger a re-render. You should never need to call forceUpdate().
Finally, your render() function should return its content based SOLELY on this.props and this.state. That is, for any given props and state, the output will be predictable. Think of this.props and this.state as input parameters to the render() function. That is all render() can, or needs to, know about.
I won't try to write the render() function as I'm not sure exactly what you're trying to achieve with this component. But for a given this.props.input and this.state.blockCount (or whatever you choose to use as props and state) you should know exactly what you're outputting.
I know I haven't directly answered the question you put, but I hope this clarifies some React concepts.
We use Backbone + ReactJS bundle to build a client-side app.
Heavily relying on notorious valueLink we propagate values directly to the model via own wrapper that supports ReactJS interface for two way binding.
Now we faced the problem:
We have jquery.mask.js plugin which formats input value programmatically thus it doesn't fire React events. All this leads to situation when model receives unformatted values from user input and misses formatted ones from plugin.
It seems that React has plenty of event handling strategies depending on browser. Is there any common way to trigger change event for particular DOM element so that React will hear it?
For React 16 and React >=15.6
Setter .value= is not working as we wanted because React library overrides input value setter but we can call the function directly on the input as context.
var nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
nativeInputValueSetter.call(input, 'react 16 value');
var ev2 = new Event('input', { bubbles: true});
input.dispatchEvent(ev2);
For textarea element you should use prototype of HTMLTextAreaElement class.
New codepen example.
All credits to this contributor and his solution
Outdated answer only for React <=15.5
With react-dom ^15.6.0 you can use simulated flag on the event object for the event to pass through
var ev = new Event('input', { bubbles: true});
ev.simulated = true;
element.value = 'Something new';
element.dispatchEvent(ev);
I made a codepen with an example
To understand why new flag is needed I found this comment very helpful:
The input logic in React now dedupe's change events so they don't fire
more than once per value. It listens for both browser onChange/onInput
events as well as sets on the DOM node value prop (when you update the
value via javascript). This has the side effect of meaning that if you
update the input's value manually input.value = 'foo' then dispatch a
ChangeEvent with { target: input } React will register both the set
and the event, see it's value is still `'foo', consider it a duplicate
event and swallow it.
This works fine in normal cases because a "real" browser initiated
event doesn't trigger sets on the element.value. You can bail out of
this logic secretly by tagging the event you trigger with a simulated
flag and react will always fire the event.
https://github.com/jquense/react/blob/9a93af4411a8e880bbc05392ccf2b195c97502d1/src/renderers/dom/client/eventPlugins/ChangeEventPlugin.js#L128
At least on text inputs, it appears that onChange is listening for input events:
var event = new Event('input', { bubbles: true });
element.dispatchEvent(event);
Expanding on the answer from Grin/Dan Abramov, this works across multiple input types. Tested in React >= 15.5
const inputTypes = [
window.HTMLInputElement,
window.HTMLSelectElement,
window.HTMLTextAreaElement,
];
export const triggerInputChange = (node, value = '') => {
// only process the change on elements we know have a value setter in their constructor
if ( inputTypes.indexOf(node.__proto__.constructor) >-1 ) {
const setValue = Object.getOwnPropertyDescriptor(node.__proto__, 'value').set;
const event = new Event('input', { bubbles: true });
setValue.call(node, value);
node.dispatchEvent(event);
}
};
I know this answer comes a little late but I recently faced a similar problem. I wanted to trigger an event on a nested component. I had a list with radio and check box type widgets (they were divs that behaved like checkboxes and/or radio buttons) and in some other place in the application, if someone closed a toolbox, I needed to uncheck one.
I found a pretty simple solution, not sure if this is best practice but it works.
var event = new MouseEvent('click', {
'view': window,
'bubbles': true,
'cancelable': false
});
var node = document.getElementById('nodeMyComponentsEventIsConnectedTo');
node.dispatchEvent(event);
This triggered the click event on the domNode and my handler attached via react was indeed called so it behaves like I would expect if someone clicked on the element. I have not tested onChange but it should work, and not sure how this will fair in really old versions of IE but I believe the MouseEvent is supported in at least IE9 and up.
I eventually moved away from this for my particular use case because my component was very small (only a part of my application used react since i'm still learning it) and I could achieve the same thing another way without getting references to dom nodes.
UPDATE:
As others have stated in the comments, it is better to use this.refs.refname to get a reference to a dom node. In this case, refname is the ref you attached to your component via <MyComponent ref='refname' />.
You can simulate events using ReactTestUtils but that's designed for unit testing.
I'd recommend not using valueLink for this case and simply listening to change events fired by the plugin and updating the input's state in response. The two-way binding utils more as a demo than anything else; they're included in addons only to emphasize the fact that pure two-way binding isn't appropriate for most applications and that you usually need more application logic to describe the interactions in your app.
For HTMLSelectElement, i.e. <select>
var element = document.getElementById("element-id");
var trigger = Object.getOwnPropertyDescriptor(
window.HTMLSelectElement.prototype,
"value"
).set;
trigger.call(element, 4); // 4 is the select option's value we want to set
var event = new Event("change", { bubbles: true });
element.dispatchEvent(event);
I stumbled upon the same issue today. While there is default support for the 'click', 'focus', 'blur' events out of the box in JavaScript, other useful events such as 'change', 'input' are not implemented (yet).
I came up with this generic solution and refactored the code based on the accepted answers.
export const triggerNativeEventFor = (elm, { event, ...valueObj }) => {
if (!(elm instanceof Element)) {
throw new Error(`Expected an Element but received ${elm} instead!`);
}
const [prop, value] = Object.entries(valueObj)[0] ?? [];
const desc = Object.getOwnPropertyDescriptor(elm.__proto__, prop);
desc?.set?.call(elm, value);
elm.dispatchEvent(new Event(event, { bubbles: true }));
};
How does it work?
triggerNativeEventFor(inputRef.current, { event: 'input', value: '' });
Any 2nd property you pass after the 'event' key-value pair, it will be taken into account and the rest will be ignored/discarded.
This is purposedfully written like this in order not to clutter arguments definition of the helper function.
The reason as to why not default to get descriptor for 'value' only is that for instance, if you have a native checkbox <input type="checkbox" />, than it doesn't have a value rather a 'checked' prop/attribute. Then you can pass your desired check state as follows:
triggerNativeEventFor(checkBoxRef.current, { event: 'input', checked: false });
I found this on React's Github issues: Works like a charm (v15.6.2)
Here is how I implemented to a Text input:
changeInputValue = newValue => {
const e = new Event('input', { bubbles: true })
const input = document.querySelector('input[name=' + this.props.name + ']')
console.log('input', input)
this.setNativeValue(input, newValue)
input.dispatchEvent(e)
}
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)
}
}
Triggering change events on arbitrary elements creates dependencies between components which are hard to reason about. It's better to stick with React's one-way data flow.
There is no simple snippet to trigger React's change event. The logic is implemented in ChangeEventPlugin.js and there are different code branches for different input types and browsers. Moreover, the implementation details vary across versions of React.
I have built react-trigger-change that does the thing, but it is intended to be used for testing, not as a production dependency:
let node;
ReactDOM.render(
<input
onChange={() => console.log('changed')}
ref={(input) => { node = input; }}
/>,
mountNode
);
reactTriggerChange(node); // 'changed' is logged
CodePen
well since we use functions to handle an onchange event, we can do it like this:
class Form extends Component {
constructor(props) {
super(props);
this.handlePasswordChange = this.handlePasswordChange.bind(this);
this.state = { password: '' }
}
aForceChange() {
// something happened and a passwordChange
// needs to be triggered!!
// simple, just call the onChange handler
this.handlePasswordChange('my password');
}
handlePasswordChange(value) {
// do something
}
render() {
return (
<input type="text" value={this.state.password} onChange={changeEvent => this.handlePasswordChange(changeEvent.target.value)} />
);
}
}
The Event type input did not work for me on <select> but changing it to change works
useEffect(() => {
var event = new Event('change', { bubbles: true });
selectRef.current.dispatchEvent(event); // ref to the select control
}, [props.items]);
This ugly solution is what worked for me:
let ev = new CustomEvent('change', { bubbles: true });
Object.defineProperty(ev, 'target', {writable: false, value: inpt });
Object.defineProperty(ev, 'currentTarget', {writable: false, value: inpt });
const rHandle = Object.keys(inpt).find(k => k.startsWith("__reactEventHandlers"))
inpt[rHandle].onChange(ev);
A working solution can depend a bit on the implementation of the onChange function you're trying to trigger. Something that worked for me was to reach into the react props attached to the DOM element and call the function directly.
I created a helper function to grab the react props since they're suffixed with a hash like .__reactProps$fdb7odfwyz
It's probably not the most robust but it's good to know it's an option.
function getReactProps(el) {
const keys = Object.keys(el);
const propKey = keys.find(key => key.includes('reactProps'));
return el[propKey];
}
const el = document.querySelector('XX');
getReactProps(el).onChange({ target: { value: id } });
Since the onChange function was only using target.value I could pass a simple object to onChange to trigger my change.
This method can also help with stubborn react owned DOM elements that are listing for onMouseDown and do not respond to .click() like you'd expect.
getReactProps(el).onMouseDown(new Event('click'));
If you are using Backbone and React, I'd recommend one of the following,
Backbone.React.Component
react.backbone
They both help integrate Backbone models and collections with React views. You can use Backbone events just like you do with Backbone views. I've dabbled in both and didn't see much of a difference except one is a mixin and the other changes React.createClass to React.createBackboneClass.