ReactJS - setState() is not updating - reactjs

I have a single page create-react-app project and the issue I am having is that the state is not updating. I have cut some of my code out just to make the example a bit shorter and easier to follow. The function where setState() is called is in form_change().
My goal here is to change the color of the text when there is an error, but even {this.state.test} isn't updating. I have tried putting console.log()s in various locations to get around the async nature of setState, but unfortunately they seem to show that state is never updated. It has been a while since I have used React, so there is a chance I am doing something very silly!
Thanks in advance for your help.
Here is my code:
import React,{Component} from 'react';
import './App.css';
import Dropdown from 'react-dropdown'
import classes from './classes.module.css'
import firebase from 'firebase';
import 'react-dropdown/style.css';
const axios = require('axios');
class App extends Component {
render(){
const error_empty = (param)=>{
if (this.state.error===undefined){
return false
}
else{
if (this.state.errors.find(el => el === param) === undefined){
return false
}
else return true
}
}
const form_change = (event, param)=>{
let form={...this.state.form};
form[param]=event.target.value;
let errors =verified(form);
console.log(form); //as expected
console.log(errors); //as expected
//works up til here. setState not updating for some reason.
this.setState({form:form,errors:errors,test:'Hello World'})
}
const verified = (data)=>{
let errors = [];
let form = data;
errors.push('ean')
return errors}
this.state = {
example:['abc'],
form:{
example:"abc"
}
}
return (
<div className="App">
<header className="App-header">
<div className={classes.button_holder}>
<div className={classes.page_button} onClick={()=>{null}}>
{this.state.test}
</div>
</div>
<div className={classes.user_form}>User Update Form
<div>
<input className={classes.input_text} style={{color: error_empty()?'red':'black'}} value={this.state.form.ean} onChange={(event)=>{form_change(event,'ean')}} placeholder={"EAN"}></input>
</div>
</div>
</header>
</div>
);
}
}
export default App;

Move initialization of state outside render function this.state.

Initialise your state in constructor
Don't update your state (this.setState) in render because this will lead to infinitive loop.
Move your functions error_empty(), form_change() and verified() outside the render.
To call function onChange use this
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
example:['abc'],
form:{
example:"abc"
}
error: ""
}
}
error_empty = (param) => {
if(this.state.error===undefined) {
return false
}
else {
if(this.state.errors.find(el => el === param) === undefined) {
return false
}
return true
}
}
...
render() {
...
<input ... onChange={(event) => {this.form_change(event,'ean')}}/>
...
}
}

Related

componentWillUpdate infinite loop when setState

I need to listen to changes in the state, and if I change it, pass it to the props, but when I try to make the setState an infinite loop and an error begins. Is it possible to make a condition so that there is no error or can there be another way to transfer data from the component?
ProfileTag.js
import React, {Component} from 'react';
import classnames from 'classnames';
import './ProfileTag.scss';
import Tag from './Tag';
class ProfileTag extends Component {
state = {
tags: this.props.tags
}
onKeyUp = (e) => {
// Space -> 32 and enter is 13
if (e.which === 32 || e.which === 13) {
let input = e.target.value.trim().split(" ")[0];
if (input.length === 0 || input[0] === "") return; // empty tags
this.setState({
tags: [...this.state.tags, input]
});
e.target.value = "";
}
}
onDeleteTag = (tag) => {
const tags = this.state.tags.filter((t) => {
return (t !== tag);
});
// console.log("tags: ", tags);
this.setState({tags: tags});
}
componentWillUpdate(nextProps, nextState) {
console.log(nextProps)
console.log(nextState)
this.props.getTags(nextState.tags);
}
render() {
const tags = this.state.tags.map((tag, i) => {
return <Tag
onDeleteTag={this.onDeleteTag}
key={i}
value={tag}/>
});
return (
<div className="ProfileTag">
<label className="ProfileTag__title"
htmlFor={this.props.name}>{this.props.label}</label>
<input
onKeyUp={(e) => this.onKeyUp(e)}
type={this.props.type}
className={classnames('ProfileTag__input')}
placeholder={this.props.placeholder}
name={this.props.name}
id={this.props.name}
value={this.props.value}
disabled={this.props.disabled}
/>
<ul className="ProfileTag__tags">{tags}</ul>
</div>
)
}
}
export default ProfileTag;
Here error:
import React, {Component, Fragment} from 'react';
import connect from 'react-redux/es/connect/connect';
import ProfileTag from '../common/Tag/ProfileTag';
class Profile extends Component {
constructor() {
super();
this.state = {};
}
getTags(tags) {
console.log(tags)
// Here error
this.setState({tata: 'vava'});
}
render() {
let profileContent = (
<div className="Profile">
<ProfileTag
label="Введите теги (ваша основная специальность)"
placeholder="Тег"
name="test1"
tags={this.state.profile.tags}
getTags={this.getTags.bind(this)}
/>
</div>
);
return (
<Fragment>
{profileContent}
</Fragment>
);
}
}
const mapStateToProps = state => ({
profile: state.profile
});
export default connect(mapStateToProps, {getCurrentProfile, postProfile})(Profile);
Two things you should change
componentWillUpdate is deprecated in latest versions, use componentDidUpdate instead
Do not call a function from componentDidUpdate which calls setState directly otherwise your code will go into an infinite loop
Code example
componentDidUpdate(prevProps, prevState) {
if(prevState.tags !== this.state.tags) { // you can use isEqual from lodash for a equality check on arrays or objects
this.props.getTags(this.state.tags);
}
}
in componentDidUpdate ,never use setState,every time when it changes state which would be continuous ,it will go in infinite loop. You can check the with prevprops in componentDidUpdate(object prevProps, object prevState), and make sure you have new props in your hand.
Outside of your componentdidupdate ,create a callback function and with prevprops you can setstate there.

Maximum update depth exceeded - React Js

This error :
Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
why is this error?
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(props){
super(props);
this.state=({
todos:[],
})
this.add = this.add.bind(this);
this.remove = this.remove.bind(this);
}
//this.array.splice(2,1)
/*
let arrayy = [...this.state.array]
let removed = arrayy.splice(deger,1);
this.setState({
array:arrayy,
})
*/
add(){
const deger = document.getElementById('deger').value;
const todosarray = this.state.todos;
todosarray.push(deger)
this.setState({
todos:todosarray,
})
}
remove(num){
let arrayy = [...this.state.todos]
arrayy.splice(num,1)
this.setState({
array:arrayy,
})
}
render() {
const data = this.state.todos;
const listItems = data.map((result,i) => <li key={i} onClick={this.remove(i)}>{result }</li>);
return (
<div >
<input id="deger"></input>
<button onClick={this.add}>Add</button>
<div id="items">
{listItems}
</div>
</div>
);
}
}
export default App;
Replace this:
onClick={this.remove(i)}
By this:
onClick={() => this.remove(i)}
Explanation: while rendering, React evaluates this.remove(i), which changes the state or the props, thus triggering another render, and looping to re-evaluate this.remove(i); creating a (hidden) infinite loop. () => this.remove(i) is a function so the state or the props do not change. Also, it's probably what you wanted to code anyway ;)

Should componentDidMount run from connectStateResults?

I'm trying to create an infinite siema carousel using algolia's instantsearch in react, but I don't think the connectors behave like React components. Should I expect componentDidMount to be called here? Suggestions? Ideas?
class ActorsClass extends connectStateResults {
constructor(props){
super(props);
var { searchState, searchResults } = props;
this.hasResults = searchResults && searchResults.nbHits !== 0;
}
componentDidMount() {
console.log("componentDidMount " + this.props.siema)
this.siema = new Siema(this.props.siema);
}
prev = () => {
this.siema.prev()
};
next = () => {
this.siema.next()
};
render = () => {
return (
<div className="actors-container">
<div xhidden={!this.hasResults}>
<h1>Actors</h1>
<InfiniteHits hitComponent={HitActors} />
</div>
<button onClick={this.prev}>Prev</button>
<button onClick={this.next}>Next</button>
</div>
);
}
Whenever the connected component receives new props they are re-invoked. It means you can use componentDidUpdate hook for your use case.
You may be interested to use reselect. See the docs for using selector.

How can I use directly function in React?

In this code, I would like to show data with directly using function _renderMovies
not like
{movies? this._renderMovies(): 'Loading!' }
cuz I don't want to show Loadings
Do you guys have an idea of how can I use directly function _renderMovies?
My code :
import React, { Component } from 'react';
import L_MovieList from './L_MovieList';
import L_Ranking from './L_Ranking';
import './L_BoxOffice.css';
class L_BoxOffice extends Component {
state ={
}
constructor(props) {
super(props);
this._renderMovies = this._renderMovies.bind(this);
}
componentDidMount(){
this._getMovies();
}
_renderMovies=()=>{
const movies= this.state.movies.map((movie)=>{
console.log(movie)
return <L_Ranking
title={movie.title_english}
key={movie.id}
genres={movie.genres}
/>
})
return movies
}
_getMovies = async()=>{
const movies = await this._callApi()
this.setState({
//movies : movies
movies
})
}
_callApi=()=>{
return fetch('https://yts.am/api/v2/list_movies.json?sort_by=download_count')
.then(potato=> potato.json())
.then(json=> json.data.movies)
.catch(err=>console.log(err))
}
render() {
const{movies}=this.state;
return (
<div>
<div className={movies ? "L_BoxOffice" : "L_BoxOffice--loading"}>
<div className="L_Ranking_title">RANKING</div>
{movies? this._renderMovies(): 'Loading!' }
</div>
Box office page
<L_MovieList/>
</div>
);
}
}
export default L_BoxOffice;
First of all set movies to be an empty array by default in the state.
constructor(props) {
super(props);
this.state = { movies: [] }
this._renderMovies = this._renderMovies.bind(this);
}
After that just render the movies:
<div className="L_Ranking_title">RANKING</div>
{this._renderMovies()}
</div>
Having an empty array as a default value, will remove the ternary operator usage and .map will always work, because by default the movies will be iterable.
Replace {movies? this._renderMovies(): 'Loading!' } with _this.renderMovies()
use arrow function
like you can directly call any function like this
_renderMovies = ()=>{
if(true){
}else{
return 'loading...' //just an example
}
}
render(){
{this._renderMovies()}
}

How to handle onChange event that sets the state in React?

I am learning React and in the below code I get ...cannot update during an existing state transition.... While looking to fix it, I read in SO that setState should not be used within render() as it calls render() repeatedly resulting in infinite loop. But I dont know how to fix it.
import React from 'react';
import ReactDOM from 'react-dom';
export default class CheckBox extends React.Component{
constructor() {
super();
this.state = {isChecked: false};
}
handleChecked () {
this.setState({isChecked: !this.state.isChecked});
}
render(){
var txt;
if (this.state.isChecked) {
txt = 'checked'
} else {
txt = 'unchecked'
}
return(
<div>
<input type="checkbox" onChange={this.handleChecked()}/>
<p>This box is {txt}</p>
</div>
);
}
}
ReactDOM.render(<CheckBox/>, document.getElementById('hello'));
You should pass to onChange reference to function but not call it., in your example you are calling handleChecked(because there is () after function) and result pass to onChange however result in this case will be undefined so onChange looks like onChange={ undefined }. Also, you can't set state during the initial render, but you are trying to do it with this.handleChecked() which contains this.setState.
export default class CheckBox extends React.Component{
constructor() {
super();
this.state = {isChecked: false};
// set this (with .bind),
// because you need get methods from CheckBox, like .setState
this.handleChecked = this.handleChecked.bind(this);
}
handleChecked () {
this.setState({isChecked: !this.state.isChecked});
}
render(){
var txt;
if (this.state.isChecked) {
txt = 'checked'
} else {
txt = 'unchecked'
}
// remove () after handleChecked because you need pass
// reference to function
// also add return statement before <div>
return <div>
<input type="checkbox" onChange={ this.handleChecked }/>
<p>This box is {txt}</p>
</div>
}
}
Example

Resources