avoid constant re-render from "input" or "textarea" in react js - reactjs

Currently in react js, when I want to bind a text area or an input with a "state", I will need to set the onChange method and setState() everytime user type in a single letter
I heard if you setState react js refresh and re-render everything in this component
Is there any more efficient way to do so? using "shouldComponentUpdate" will be improper in this case since if I don't make "state" update, all user input will be stuck..

Well, that's how you implement controlled input elements in React.
However, if performance is a major concern of yours, you could either isolate your input element in a separate stateful component, hence only triggering a re-render on itself and not on your entire app.
So something like:
class App extends Component {
render() {
return (
<div>
...
<MyInput />
...
</div>
);
}
}
class MyInput extends Component {
constructor() {
super();
this.state = {value: ""};
}
update = (e) => {
this.setState({value: e.target.value});
}
render() {
return (
<input onChange={this.update} value={this.state.value} />
);
}
}
Alternatively, you could just use an uncontrolled input element. For example:
class App extends Component {
render() {
return (
<div>
...
<input defaultValue="" />
...
</div>
);
}
}
Though, note that controlled inputs are generally recommended.

As #Chris stated, you should create another component to optimize the rerendering to only the specified component.
However, there are usecases where you need to update the parent component or dispatch an action with the value entered in your input to one of your reducers.
For example I created a SearchInput component which updates itself for every character entered in the input but only call the onChange function only if there are 3 characters at least.
Note: The clearTimeout is useful in order to call the onChange function only when the user has stopped typing for at least 200ms.
import React from 'react';
class SearchInput extends React.Component {
constructor(props) {
super(props);
this.tabTimeoutId = [];
this.state = {
value: this.props.value,
};
this.onChangeSearch = this.onChangeSearch.bind(this);
}
componentWillUpdate() {
// If the timoutId exists, it means a timeout is being launch
if (this.tabTimeoutId.length > 1) {
clearTimeout(this.tabTimeoutId[this.tabTimeoutId.length - 2]);
}
}
onChangeSearch(event) {
const { value } = event.target;
this.setState({
value,
});
const timeoutId = setTimeout(() => {
value.length >= this.props.minSearchLength ? this.props.onChange(value) : this.props.resetSearch();
this.tabTimeoutId = [];
}, this.props.searchDelay);
this.tabTimeoutId.push(timeoutId);
}
render() {
const {
onChange,
minSearchLength,
searchDelay,
...otherProps,
} = this.props;
return <input
{...otherProps}
value={this.state.value}
onChange={event => this.onChangeSearch(event)}
/>
}
}
SearchInput.propTypes = {
minSearchLength: React.PropTypes.number,
searchDelay: React.PropTypes.number,
};
SearchInput.defaultProps = {
minSearchLength: 3,
searchDelay: 200,
};
export default SearchInput;
Hope it helps.

You need to bind the onChange() event function inside constructor like as code snippets :
class MyComponent extends Component {
constructor() {
super();
this.state = {value: ""};
this.onChange = this.onChange.bind(this)
}
onChange= (e)=>{
const formthis = this;
let {name, value} = e.target;
formthis.setState({
[name]: value
});
}
render() {
return (
<div>
<form>
<input type="text" name="name" onChange={this.onChange} />
<input type="text" name="email" onChange={this.onChange} />
<input type="text" name="phone" onChange={this.onChange} />
<input type="submit" name="submit" value="Submit" />
</form>
</div>
);
}
}

You don't need a complicated react solution to this problem, just a little common sense about when to update state. The best way to achieve this is to encapsulate your setState call within a timeout.
class Element extends React.Component {
onChange = (e) => {
clearTimeout(this.setStateTimeout)
this.setStateTimeout = setTimeout(()=> {
this.setState({inputValue: e.target.value})
}, 500)
}
}
This will only set state on your react element a 500ms after the last keystroke and will prevent hammering the element with rerenders as your user is typing.

Related

React onChange function does not fire when passing it down to component

Solved see comment below (Only had to move the Chooser and Section function outside of the class component to get it to work.
So I have a problem with the react onChange function. It does not seem to work when it is passed to a component as props. I tried to pass the component instead of the data but still it did not work. Please consider the following example:
export default class Parent extends Component {
constructor(props) {
super(props)
this.state = {
type: '1',
number: ''
}
}
handleChange = e => {
const { name, value } = e.target
this.setState({ [name]: value })
}
render() {
return (
<div className="App">
<Form
type={this.state.type}
number={this.state.number}
handleChange={this.handleChange}
/>
</div>
}
}
//receiving the props
export default class Child extends Component {
constructor(props) {
super(props)
}
render() {
const Chooser = ({ type, section }) => {
switch (type) {
case '1':
return <Fragment>{section}</Fragment>
default:
return <Fragment>></Fragment>
}
}
const Section = ({ number, handleChange }) => (
<Fragment>
<div>
<label>Number</label>
<input
type='text'
name='number'
placeholder='123456789'
value={number}
onChange={handleChange}
/>
</div>
</Fragment>
)
return (
<Chooser
type={this.props.type}
section={
<Section
number={this.props.number}
handleChange={this.props.handleChange}
/>
}
/>
)
}
}
Interestingly if I put the onChange on the Section level it does work. But this is not what I want since a passed component could have multiple Input functions that I want to pass.
return (
<Chooser
type={this.props.type}
section={
<Section
number={this.pops.number}
onChange={this.pops.handleChange}
/>
}
/>
Any ideas how I can pass the onChange function down using props? On a similar example the Input change does work but it is loosing focus each time a value is pressed. Already tried assigning keys but that did not work either.
You currently have this as your handleChange method
handleChange = e => {
const { name, value } = e.target.value
this.setState({ [name]: value })
}
You should change it to this.
handleChange = e => {
const { name, value } = e.target;
this.setState({ [name]: value })
}
You seem to be accessing the wrong property in the target because name will always be undefined inside e.target.value and as such, calling setState won't do anything.
Also, you should probably be declaring your function components outside of the class component.
this is because you need to destructure it like this on the next like
const {handleChange} = this.props.handleChange
You can wrap handleChange() in an anonymous function so that it will actively wait for you to make changes to the input. Otherwise it will run on render.
const Section = ({ number, handleChange }) => (
<Fragment>
<div>
<label>Number</label>
<input
type='text'
name='number'
placeholder='123456789'
value={number}
onChange={(e) => handleChange(e)}
/>
</div>
</Fragment>
)

React pass multi state between two components

i found a gist about how to pass state between two components.
Here the jsbin
But how about the multi state?
I want two input fields and show the entered text in other components when i edit it.
i tried edited like this
this.state = {
fieldVal: "" //first input state
otherFieldVal: "" //second
}
and
//input onChange
onUpdate = name => (event) => {
this.setState({ [name]: event.target.value });
};
with no luck.
How can i made it work on multi state for multi input fields ?
Don't need to keep state in both Child and parent. You can write your child component like below, and you can access tow states dynamically by using data-attirb or you can folloe #Isaac 's answer.Keep the state in Child and pass state to Parent or keep the event to Parent from Child.
export class Child extends React.Component {
update = (e) => {
this.props.onUpdate(e.target)
};
render() {
return (
<div>
<h4>Child</h4>
<input
type="text"
placeholder="type here"
onChange={this.update}
data-state = "fieldVal"
value={this.props.fieldVal}
/><br/><br/>
<input
type="text"
placeholder="type here"
onChange={this.update}
data-state = "otherFieldVal"
value={this.props.otherFieldVal}
/>
</div>
)
}
}
export class OtherChild extends React.Component {
render() {
return (
<div>
<h4>OtherChild</h4>
Value in OtherChild Props passedVal1: {this.props.passedVal1} <br/>
Value in OtherChild Props passedVal2: {this.props.passedVal2}
</div>
)
}
}
and in parent :
class App extends Component {
onUpdate = (data) => {
this.setState({
[data.dataset.state]: data.value
})
};
render() {
return (
<div>
<h2>Parent</h2>
Value in Parent Component State fieldVal: {this.state.fieldVal} <br/>
Value in Parent Component State otherFieldVal: {this.state.otherFieldVal}
<br/>
<Child onUpdate={this.onUpdate} fieldVal= {this.state.fieldVal} otherFieldVal ={this.state.otherFieldVal}/>
<br />
<OtherChild passedVal1={this.state.fieldVal} passedVal2={this.state.otherFieldVal}/>
</div>
);
}
}
demo
renderInput = (prop) => {
return (
<Input
onChange={(event) => {
this.setState({ [prop]: event.target.value });
}}
/>
)
}
render() {
<div>
{this.renderInput('name')}
{this.renderInput('age')}
</div>
}
We can set a renderInput method and render different input using parameter to achieve your objective

React: state inside children not updating through props

I want to set the state value of children component using props and then display the same for the respective form field. I did that inside the componentDidMount() but it doesn't work.
Code:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
tweetCount: this.props.tweetCount
};
}
componentDidMount() {
const tweetCount = this.props.tweetCount;
this.setState({ tweetCount });
}
render() {
return (
<React.Fragment>
<Form>
<FormGroup>
<Label for="tweetCount">
Tweets per Column (between 1 and 30):
{this.state.tweetCount}
</Label>
<Input
id="tweetCount"
type="range"
min="1"
max="30"
value={this.state.tweetCount}
onChange={this.changeTweetCount}
step="1"
/>
</FormGroup>
</Form>
</React.Fragment>
);
}
}
If you use React that version is upper than 16.3 then use getDerivedStateFromProp so inside of using componentDidMount or componentWillReceiveProps use this code :
static getDerivedStateFromProps(props, state)
{
return{
tweetCount:props.tweetCount
}
}

[admin-on-rest]-"Warning" when create <SimpleForm/> with ImageInput [duplicate]

I am looking to create a stateless component who's input element can be validated by the parent component.
In my example below, I am running into a problem where the input ref is never being assigned to the parent's private _emailAddress property.
When handleSubmit is called, this._emailAddress is undefined. Is there something I'm missing, or is there a better way to do this?
interface FormTestState {
errors: string;
}
class FormTest extends React.Component<void, FormTestState> {
componentWillMount() {
this.setState({ errors: '' });
}
render(): JSX.Element {
return (
<main role='main' className='about_us'>
<form onSubmit={this._handleSubmit.bind(this)}>
<TextInput
label='email'
inputName='txtInput'
ariaLabel='email'
validation={this.state.errors}
ref={r => this._emailAddress = r}
/>
<button type='submit'>submit</button>
</form>
</main>
);
}
private _emailAddress: HTMLInputElement;
private _handleSubmit(event: Event): void {
event.preventDefault();
// this._emailAddress is undefined
if (!Validators.isEmail(this._emailAddress.value)) {
this.setState({ errors: 'Please enter an email address.' });
} else {
this.setState({ errors: 'All Good.' });
}
}
}
const TextInput = ({ label, inputName, ariaLabel, validation, ref }: { label: string; inputName: string; ariaLabel: string; validation?: string; ref: (ref: HTMLInputElement) => void }) => (
<div>
<label htmlFor='txt_register_first_name'>
{ label }
</label>
<input type='text' id={inputName} name={inputName} className='input ' aria-label={ariaLabel} ref={ref} />
<div className='input_validation'>
<span>{validation}</span>
</div>
</div>
);
You can useuseRef hook which is available since v16.7.0-alpha.
EDIT: You're encouraged to use Hooks in production as of 16.8.0 release!
Hooks enable you to maintain state and handle side effects in functional components.
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` points to the mounted text input element
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
Read more in Hooks API documentation
EDIT: You now can with React Hooks. See the answer by Ante Gulin.
You can't access React like methods (like componentDidMount, componentWillReceiveProps, etc) on stateless components, including refs. Checkout this discussion on GH for the full convo.
The idea of stateless is that there isn't an instance created for it (state). As such, you can't attach a ref, since there's no state to attach the ref to.
Your best bet would be to pass in a callback for when the component changes and then assign that text to the parent's state.
Or, you can forego the stateless component altogether and use an normal class component.
From the docs...
You may not use the ref attribute on functional components because they don't have instances. You can, however, use the ref attribute inside the render function of a functional component.
function CustomTextInput(props) {
// textInput must be declared here so the ref callback can refer to it
let textInput = null;
function handleClick() {
textInput.focus();
}
return (
<div>
<input
type="text"
ref={(input) => { textInput = input; }} />
<input
type="button"
value="Focus the text input"
onClick={handleClick}
/>
</div>
);
}
This is late but I found this solution much better.
Pay attention to how it uses useRef & how properties are available under current property.
function CustomTextInput(props) {
// textInput must be declared here so the ref can refer to it
const textInput = useRef(null);
function handleClick() {
textInput.current.focus();
}
return (
<div>
<input
type="text"
ref={textInput} />
<input
type="button"
value="Focus the text input"
onClick={handleClick}
/>
</div>
);
}
For more reference check react docs
The value of your TextInput is nothing more than a state of your component. So instead of fetching the current value with a reference (bad idea in general, as far as I know) you could fetch the current state.
In a reduced version (without typing):
class Form extends React.Component {
constructor() {
this.state = { _emailAddress: '' };
this.updateEmailAddress = this.updateEmailAddress.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
updateEmailAddress(e) {
this.setState({ _emailAddress: e.target.value });
}
handleSubmit() {
console.log(this.state._emailAddress);
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input
value={this.state._emailAddress}
onChange={this.updateEmailAddress}
/>
</form>
);
}
}
You can also get refs into functional components with a little plumbing
import React, { useEffect, useRef } from 'react';
// Main functional, complex component
const Canvas = (props) => {
const canvasRef = useRef(null);
// Canvas State
const [canvasState, setCanvasState] = useState({
stage: null,
layer: null,
context: null,
canvas: null,
image: null
});
useEffect(() => {
canvasRef.current = canvasState;
props.getRef(canvasRef);
}, [canvasState]);
// Initialize canvas
useEffect(() => {
setupCanvas();
}, []);
// ... I'm using this for a Konva canvas with external controls ...
return (<div>...</div>);
}
// Toolbar which can do things to the canvas
const Toolbar = (props) => {
console.log("Toolbar", props.canvasRef)
// ...
}
// Parent which collects the ref from Canvas and passes to Toolbar
const CanvasView = (props) => {
const canvasRef = useRef(null);
return (
<Toolbar canvasRef={canvasRef} />
<Canvas getRef={ ref => canvasRef.current = ref.current } />
}

Why TextBox Cleared after submit reactjs

I am trying to pass data from one component to another. but it has no parent child relation and it is independent from each other. i am able to set the state but problem is after clicking enter my text data get cleared. not sure why,
export class EmpSearch extends React.Component {
// Not needed anymore as state going to Redux and not local component state
/*
constructor(props) {
super(props);
this.state = {
Empnumber: ''
};
}
*/
EmpSearch = (e) => {
if (e.key === 'Enter') {
browserHistory.push('/Emp/' + e.target.value);
}
}
updateEmpNumber(e) {
this.props.dispatch({
type: 'UPDATE_EMP_NUMBER',
payload: e.target.value
});
}
render() {
return (
<div className="row">
<form>
<div className="form-group">
<label htmlFor="Empnumber">Emp Number</label>
<input type="text" className="form-control" id="Empnumber" placeholder="Emp Number" value={this.props.Empnumber} onChange={this.updateEmpNumber.bind(this)} onKeyPress={this.EmpSearch}/>
</div>
</form>
</div>
);
}
}
function mapStateToProps(state){
return {
Empnumber: state.Empnumber
}
}
export default connect(mapStateToProps)(EmpSearch);
browserHistory.push('/Emp/' + e.target.value); is probably causing your component to be unmounted and remounted.
You say the value is stored in the redux state, but I can't see where you update the redux state.

Resources