Trying to create a delay on react component that has input field that updates on change
Here is my onChange method
handleOrderQtyKeyPress (e) {
var regex = /[^0-9]/
if (e.key.match(regex)) {
e.preventDefault();
}
if (this.state.orderQtyValue.toString().length == 3) {
e.preventDefault();
}
}
and the react-bootstrap component:
<FormControl
type='number'
min='0'
value={this.state.orderQtyValue}
onChange={this.handleOrderQtyChange}
onKeyPress={this.handleOrderQtyKeyPress}
style={styles.orderQtyValue}
/>
so I tried importing lodash _.debounce and applying at the constructor
import debounce from 'lodash/debounce';
this.handleOrderQtyKeyPress = _.debounce(this.handleOrderQtyKeyPress.bind(this),1000);
I am not getting a debounce. What am I missing here?
I see that you use this, so I assume that FormControl is inside of a render function of your stateful component. In this case make sure that your binding and debouncing is happening in constructor of this stateful component.
```
const Component extends React.Component {
constructor(props) {
super(props);
this.handleOrderQtyKeyPress = _.debounce(this.handleOrderQtyKeyPress.bind(this), 1000);
}
}
```
Please, read comment which explains how this works
class Input extends Component {
static propTypes = {
onChange: PropTypes.func.isRequired,
value: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.number,
]),
}
state = {
value: '',
}
// When component receives updated `value` from outside, update internal `value` state.
componentWillReceiveProps(nextProps) {
this.setState({ value: nextProps.value });
}
// Store updated value localy.
onChange = (event) => {
this.setState({ value: event.target.value });
}
onBlur = (event) => {
// Trigger change to external env.
this.props.onChange(this.state.value);
// Also, don't forget to call `onBlur` if received from parent.
if (this.props.onBlur) {
this.props.onBlur(event);
}
}
render() {
return <input {...this.props} value={this.state.value} onChange={this.onChange} onBlur={this.onBlur} />
}
}
If you want to automatically debounce (or throttle) a component easily, when the props change often (as opposed to internal state changed),
I've authored a tiny wrapper (1kb minified) for that, called React-Bouncer:
import bouncer from '#yaireo/react-bouncer'
// uses 300ms `debounce` by default
const DebouncedYourComponent = bouncer(YourComponent)
This is useful when you do not have much of a control on the rate which the props sent to the component are updated, or the root cause of the often updates is unknown.
(Obviously, using debounce on the root-cause is the first thing to try)
Related
I'm using a high order component that is not rendering the child on a render change. The code below has been chopped down for simplicity sake.
The HOC that looks like this:
const withMyComponent = (WrapperComponent) => {
class HOC extends Component {
state = {
value: ''
};
changedHandler = (event) => {
this.setState({ value: event.target.value });
};
render() {
return (<WrapperComponent {...this.props}
changed={this.changedHandler}
value={this.state.value} />);
}
};
return HOC;
};
export default withMyComponent;
Then I have a child component that uses this HOC:
class myChildComponent extends Component {
render() {
return (
<input type="text"
onChange={(event) => this.props.changed(event)}
value={this.props.value || ''} />
);
};
};
export default withMyComponent(myChildComponent);
The problem I am experiencing is that the input is not updating with the new value that is passed back from the HOC. In fact, the child render is not even firing after the initial mount and doesn't seem to fire on any changed event.
I have placed debugging and console.logs in the HOC render and the changed event and the render are firing with the proper data.
So, if I type in the textbox, it fires the change event in the HOC and updates the state in the HOC and fires the render event in the HOC. But it is not firing the wrapped components render event, and thus the textbox never updates with any values.
Anyone able to solve this issue or lead me in the right direction?
Thanks again.
You need to pass an object to this.setState
const withMyComponent = WrapperComponent => {
class HOC extends Component {
state = {
value: ""
};
changedHandler = event => {
// Instead of
// this.setState( value: event.target.value );
// Do This.
this.setState({ value: event.target.value });
};
render() { ... }
}
return HOC;
};
Working Demo
I have a complete running code, but it have a flaw. It is calling setState() from inside a render().
So, react throws the anti-pattern warning.
Cannot update during an existing state transition (such as within render or another component's constructor). Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved to componentWillMount
My logic is like this. In index.js parent component, i have code as below. The constructor() calls the graphs() with initial value, to display a graph. The user also have a form to specify the new value and submit the form. It runs the graphs() again with the new value and re-renders the graph.
import React, { Component } from 'react';
import FormComponent from './FormComponent';
import PieGraph from './PieGraph';
const initialval = '8998998998';
class Dist extends Component {
constructor() {
this.state = {
checkData: true,
theData: ''
};
this.graphs(initialval);
}
componentWillReceiveProps(nextProps) {
if (this.props.cost !== nextProps.cost) {
this.setState({
checkData: true
});
}
}
graphs(val) {
//Calls a redux action creator and goes through the redux process
this.props.init(val);
}
render() {
if (this.props.cost.length && this.state.checkData) {
const tmp = this.props.cost;
//some calculations
....
....
this.setState({
theData: tmp,
checkData: false
});
}
return (
<div>
<FormComponent onGpChange={recData => this.graphs(recData)} />
<PieGraph theData={this.state.theData} />
</div>
);
}
}
The FormComponent is an ordinary form with input field and a submit button like below. It sends the callback function to the Parent component, which triggers the graphs() and also componentWillReceiveProps.
handleFormSubmit = (e) => {
this.props.onGpChange(this.state.value);
e.preventdefaults();
}
The code is all working fine. Is there a better way to do it ? Without doing setState in render() ?
Never do setState in render. The reason you are not supposed to do that because for every setState your component will re render so doing setState in render will lead to infinite loop, which is not recommended.
checkData boolean variable is not needed. You can directly compare previous cost and current cost in componentWillReceiveProps, if they are not equal then assign cost to theData using setState. Refer below updated solution.
Also start using shouldComponentUpdate menthod in all statefull components to avoid unnecessary re-renderings. This is one best pratice and recommended method in every statefull component.
import React, { Component } from 'react';
import FormComponent from './FormComponent';
import PieGraph from './PieGraph';
const initialval = '8998998998';
class Dist extends Component {
constructor() {
this.state = {
theData: ''
};
this.graphs(initialval);
}
componentWillReceiveProps(nextProps) {
if (this.props.cost != nextProps.cost) {
this.setState({
theData: this.props.cost
});
}
}
shouldComponentUpdate(nextProps, nextState){
if(nextProps.cost !== this.props.cost){
return true;
}
return false;
}
graphs(val) {
//Calls a redux action creator and goes through the redux process
this.props.init(val);
}
render() {
return (
<div>
<FormComponent onGpChange={recData => this.graphs(recData)} />
{this.state.theData !== "" && <PieGraph theData={this.state.theData} />}
</div>
);
}
}
PS:- The above solution is for version React v15.
You should not use componentWillReceiveProps because in most recent versions it's UNSAFE and it won't work well with async rendering coming for React.
There are other ways!
static getDerivedStateFromProps(props, state)
getDerivedStateFromProps is invoked right before calling the render
method, both on the initial mount and on subsequent updates. It should
return an object to update the state, or null to update nothing.
So in your case
...component code
static getDerivedStateFromProps(props,state) {
if (this.props.cost == nextProps.cost) {
// null means no update to state
return null;
}
// return object to update the state
return { theData: this.props.cost };
}
... rest of code
You can also use memoization but in your case it's up to you to decide.
The link has one example where you can achieve the same result with memoization and getDerivedStateFromProps
For example updating a list (searching) after a prop changed
You could go from this:
static getDerivedStateFromProps(props, state) {
// Re-run the filter whenever the list array or filter text change.
// Note we need to store prevPropsList and prevFilterText to detect changes.
if (
props.list !== state.prevPropsList ||
state.prevFilterText !== state.filterText
) {
return {
prevPropsList: props.list,
prevFilterText: state.filterText,
filteredList: props.list.filter(item => item.text.includes(state.filterText))
};
}
return null;
}
to this:
import memoize from "memoize-one";
class Example extends Component {
// State only needs to hold the current filter text value:
state = { filterText: "" };
// Re-run the filter whenever the list array or filter text changes:
filter = memoize(
(list, filterText) => list.filter(item => item.text.includes(filterText))
);
handleChange = event => {
this.setState({ filterText: event.target.value });
};
render() {
// Calculate the latest filtered list. If these arguments haven't changed
// since the last render, `memoize-one` will reuse the last return value.
const filteredList = this.filter(this.props.list, this.state.filterText);
return (
<Fragment>
<input onChange={this.handleChange} value={this.state.filterText} />
<ul>{filteredList.map(item => <li key={item.id}>{item.text}</li>)}</ul>
</Fragment>
);
}
}
I am new to react and so confused in handling and calling the onChange events.
Now , I have 2 components :-
1. Parent component -
updateField = e => {
console.log("update field e called");
this.setState({
value: e.target.value
});
};
<InputTypeahead value={this.state.value} label="Email" onChange={this.updateField} typeaheadItems={this.emailAdressess} /
where I am calling the onChange and taking the current value out. Till
now whatever I type in Input I get the value.
Now,
2.In Child component :
I want to take the value coming from this parent component and using that would like to setstate.
How to achieve this in React js ? I have tried using refs , but result was not successful.
Any Help is appreciated.Thanks.
From version i.e 16.3.0 onwards, you can make use of getDerivedStateFromProps method to update the state based on props like
class InputTypeahead extends React.Component {
state = {
value: ''
}
static getDerivedStateFromProps(nextProps, prevState) {
if(nextProps.value !== prevState.value) {
return { value: nextProps.value};
}
return null;
}
}
According to the docs:
getDerivedStateFromProps is invoked after a component is
instantiated as well as when it receives new props. It should return
an object to update state, or null to indicate that the new props do
not require any state updates.
Before v16.3.0, you would make use of constructor along with componentWillReceiveProps like
class InputTypeahead extends React.Component {
constructor(props) {
super(props);
this.state = {
value: props.value
}
}
componentWillReceiveProps(nextProps) {
if(nextProps.value !== this.props.value) {
this.setState({ value: nextProps.value});
}
}
}
I am currently grabbing a prop from state and using it on an event listener. i.e.,
import * as React from 'react';
import { getDetails } from './actions';
interface Props {
selecting: boolean;
getDetails(): Action<void>;
}
#connect((state) => ({
selecting: state.items.selecting,
}), {
getDetails,
})
export default class Grid extends React.PureComponent<Props> {
onMouseEnter = () => {
if (!this.props.selecting) {
this.props.getDetails();
}
}
render() {
return (
<div onMouseEnter={this.onMouseEnter} />
);
}
}
However, whenever the selecting property changes, it causes a re-render to my component.
Is there a way to pass a variable from state through connect and NOT have it trigger this update to my component? I want it almost as if it were an instance-bound variable rather than a state variable.
Try overriding the shouldComponentUpdate() lifecycle function. This gives you much more granular control over when your component should or shouldn't re-render (at the cost of added code complexity).
shouldComponentUpdate(nextProps, nextState) {
if(nextProps.someLogic !== this.props.someLogic)
return false; // Don't re-render
return true;
}
Documentation: Here
Use shouldComponentUpdate() to let React know if a component’s output is not affected by the current change in state or props. The default behavior is to re-render on every state change, and in the vast majority of cases you should rely on the default behavior.
In React, are there any real differences between these two implementations?
Some friends tell me that the FirstComponent is the pattern, but I don't see why. The SecondComponent seems simpler because the render is called only once.
First:
import React, { PropTypes } from 'react'
class FirstComponent extends React.Component {
state = {
description: ''
}
componentDidMount() {
const { description} = this.props;
this.setState({ description });
}
render () {
const {state: { description }} = this;
return (
<input type="text" value={description} />
);
}
}
export default FirstComponent;
Second:
import React, { PropTypes } from 'react'
class SecondComponent extends React.Component {
state = {
description: ''
}
constructor (props) => {
const { description } = props;
this.state = {description};
}
render () {
const {state: { description }} = this;
return (
<input type="text" value={description} />
);
}
}
export default SecondComponent;
Update:
I changed setState() to this.state = {} (thanks joews), However, I still don't see the difference. Is one better than other?
It should be noted that it is an anti-pattern to copy properties that never change to the state (just access .props directly in that case). If you have a state variable that will change eventually but starts with a value from .props, you don't even need a constructor call - these local variables are initialized after a call to the parent's constructor:
class FirstComponent extends React.Component {
state = {
x: this.props.initialX,
// You can even call functions and class methods:
y: this.someMethod(this.props.initialY),
};
}
This is a shorthand equivalent to the answer from #joews below. It seems to only work on more recent versions of es6 transpilers, I have had issues with it on some webpack setups. If this doesn't work for you, you can try adding the babel plugin babel-plugin-transform-class-properties, or you can use the non-shorthand version by #joews below.
You don't need to call setState in a Component's constructor - it's idiomatic to set this.state directly:
class FirstComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
x: props.initialX
};
}
// ...
}
See React docs - Adding Local State to a Class.
There is no advantage to the first method you describe. It will result in a second update immediately before mounting the component for the first time.
Update for React 16.3 alpha introduced static getDerivedStateFromProps(nextProps, prevState) (docs) as a replacement for componentWillReceiveProps.
getDerivedStateFromProps is invoked after a component is instantiated as well as when it receives new props. It should return an object to update state, or null to indicate that the new props do not require any state updates.
Note that if a parent component causes your component to re-render, this method will be called even if props have not changed. You may want to compare new and previous values if you only want to handle changes.
https://reactjs.org/docs/react-component.html#static-getderivedstatefromprops
It is static, therefore it does not have direct access to this (however it does have access to prevState, which could store things normally attached to this e.g. refs)
edited to reflect #nerfologist's correction in comments
You could use the short form like below if you want to add all props to state and retain the same names.
constructor(props) {
super(props);
this.state = {
...props
}
//...
}
YOU HAVE TO BE CAREFUL when you initialize state from props in constructor. Even if props changed to new one, the state wouldn't be changed because mount never happen again.
So getDerivedStateFromProps exists for that.
class FirstComponent extends React.Component {
state = {
description: ""
};
static getDerivedStateFromProps(nextProps, prevState) {
if (prevState.description !== nextProps.description) {
return { description: nextProps.description };
}
return null;
}
render() {
const {state: {description}} = this;
return (
<input type="text" value={description} />
);
}
}
Or use key props as a trigger to initialize:
class SecondComponent extends React.Component {
state = {
// initialize using props
};
}
<SecondComponent key={something} ... />
In the code above, if something changed, then SecondComponent will re-mount as a new instance and state will be initialized by props.
set the state data inside constructor like this
constructor(props) {
super(props);
this.state = {
productdatail: this.props.productdetailProps
};
}
it will not going to work if u set in side componentDidMount() method through props.
If you directly init state from props, it will shows warning in React 16.5 (5th September 2018)
you could use key value to reset state when need, pass props to state it's not a good practice , because you have uncontrolled and controlled component in one place. Data should be in one place handled
read this
https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#recommendation-fully-uncontrolled-component-with-a-key
You can use componentWillReceiveProps.
constructor(props) {
super(props);
this.state = {
productdatail: ''
};
}
componentWillReceiveProps(nextProps){
this.setState({ productdatail: nextProps.productdetailProps })
}