最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - React - Warning: An update was scheduled from inside an update function - Stack Overflow

programmeradmin0浏览0评论

I am dealing with a lot of problems about updating states in React for validation purposes, and always my state update after the render or something like that, anyway, I saw that many people solve this using a callback function in the setState, but it always throws the following warning.

Warning: An update (setState, replaceState, or forceUpdate) was scheduled from inside an update function. Update functions should be pure, with zero side-effects. Consider using ponentDidUpdate or a callback.

I tried to update inside ponentDidUpdate, but that results in an infinite loop.

This is my setState with the callback function.

state = {
    index: 0,
    activeInput: '',
    errors: {
      error: false,
      errorNombre: false,
      errorCuil: false,
      errorEmail: false,
      errorContrasena: false,
      errorCalle: false,
      errorNumero: false,
      errorProvincia: false,
      errorLocalidad: false
  },
    values: {...this.props.initialValues}
  };

    _onChangeValue = (name, value) => {
        this.setState(prevState => {
          return {
            values: {
              ...prevState.values,
              [name]: value
            }
          }, this.validate(name)
        })
      };

And this is the Validate Function, is a switch, so i much longer but the problem maybe could be that every case has another setState inside...

validate = input => {

    switch(input){
      case 'nombre':
        let { nombre } = this.state.values
        let splitNombre = nombre.split(' ');

        if ( splitNombre.length > 1 && splitNombre[1].length === 0 || nombre.length === 0 || splitNombre.length === 1 ) {
          this.setState(prevState => ({
            errors: { ...prevState.errors, error: true, errorNombre: true }
          }));
        } else {
          this.setState(prevState => ({
            errors: { ...prevState.errors, error: false, errorNombre: false }
          }));
        }
        break

As this is a pound ponent is a little messy but here is the files

Input.js

import React, { PureComponent } from 'react';

class Input extends PureComponent {
  _onChangeText = text => {
    this.props.onChangeValue(this.props.name, text);
  };

  render() {
    const { onChangeValue, name, ...rest } = this.props;
    return (
      <input {...rest} onChange={(event) => this._onChangeText(event.target.value)} />
    );
  }
}

export default Input;

Steps.js

import Botones from './Botones'

    export default class Step extends PureComponent {
        state = {}
        render() {
          return (
            <Fragment>

              {this.props.children({
                onChangeValue: this.props.onChangeValue,
                values: this.props.values,
                index: this.props.index,
                errors: this.props.errors
              })}

              <div>
            <Botones currentIndex={this.props.currentIndex}
                     prevStep={this.props.prevStep}
                     nextStep={this.props.nextStep}
                     onSubmit={this.props.onSubmit}
                     isLast={this.props.isLast}
                     isFirst={this.props.isFirst}
                     error={this.props.errors.error}    
            />

          </div>
        </Fragment>
      )
    }
  }

FirstStep.js

import Steps from './Steps';

export default class FirstStep extends Component {

    constructor(props){
        super(props)
        this.state = {}
    }

    render() {
        //console.log(this.props)
        return (
            <Fragment>
                <Steps level={this.props.index}/>
                <form onSubmit={this.onSubmit} className="mt90">
                    <div>
                        <label>Nombre pleto</label>
                        <input type="text"
                               name="nombre"
                               placeholder="Escriba su nombre"
                               onChange={ event => this.props.onChangeText('nombre', event.target.value) }
                               value={this.props.values.nombre}
                               className={ `${ this.props.errors.errorNombre && "error-focus" }` }
                               />
                        { this.props.errors.errorNombre &&
                        <span className="error">No es un nombre pleto</span> }
                    </div>
                    <div className="mt50">
                        <label>N° de CUIL</label>
                        <input type="text"
                               name="cuil"
                               placeholder="Ej.: 23-45678901-2"
                               onChange={ event => this.props.onChangeText('cuil', event.target.value) }
                               value={this.props.values.cuil}
                               className={ `${ this.props.errors.errorCuil && "error-focus" }` }
                               />
                        { this.props.errors.errorCuil &&
                        <span className="error">No es un CUIL válido</span> }
                    </div>
                </form>
            </Fragment>
        )
    }
}

App.js (has the FirstStep as a child ponent)

[...]
Render()
<RegisterBox.Step>
            { ({ values, onChangeValue, index, errors }) => (
              <FirstStep values={values} onChangeText={onChangeValue} index={index} errors={errors} />
            )}
          </RegisterBox.Step>
[...]

I am dealing with a lot of problems about updating states in React for validation purposes, and always my state update after the render or something like that, anyway, I saw that many people solve this using a callback function in the setState, but it always throws the following warning.

Warning: An update (setState, replaceState, or forceUpdate) was scheduled from inside an update function. Update functions should be pure, with zero side-effects. Consider using ponentDidUpdate or a callback.

I tried to update inside ponentDidUpdate, but that results in an infinite loop.

This is my setState with the callback function.

state = {
    index: 0,
    activeInput: '',
    errors: {
      error: false,
      errorNombre: false,
      errorCuil: false,
      errorEmail: false,
      errorContrasena: false,
      errorCalle: false,
      errorNumero: false,
      errorProvincia: false,
      errorLocalidad: false
  },
    values: {...this.props.initialValues}
  };

    _onChangeValue = (name, value) => {
        this.setState(prevState => {
          return {
            values: {
              ...prevState.values,
              [name]: value
            }
          }, this.validate(name)
        })
      };

And this is the Validate Function, is a switch, so i much longer but the problem maybe could be that every case has another setState inside...

validate = input => {

    switch(input){
      case 'nombre':
        let { nombre } = this.state.values
        let splitNombre = nombre.split(' ');

        if ( splitNombre.length > 1 && splitNombre[1].length === 0 || nombre.length === 0 || splitNombre.length === 1 ) {
          this.setState(prevState => ({
            errors: { ...prevState.errors, error: true, errorNombre: true }
          }));
        } else {
          this.setState(prevState => ({
            errors: { ...prevState.errors, error: false, errorNombre: false }
          }));
        }
        break

As this is a pound ponent is a little messy but here is the files

Input.js

import React, { PureComponent } from 'react';

class Input extends PureComponent {
  _onChangeText = text => {
    this.props.onChangeValue(this.props.name, text);
  };

  render() {
    const { onChangeValue, name, ...rest } = this.props;
    return (
      <input {...rest} onChange={(event) => this._onChangeText(event.target.value)} />
    );
  }
}

export default Input;

Steps.js

import Botones from './Botones'

    export default class Step extends PureComponent {
        state = {}
        render() {
          return (
            <Fragment>

              {this.props.children({
                onChangeValue: this.props.onChangeValue,
                values: this.props.values,
                index: this.props.index,
                errors: this.props.errors
              })}

              <div>
            <Botones currentIndex={this.props.currentIndex}
                     prevStep={this.props.prevStep}
                     nextStep={this.props.nextStep}
                     onSubmit={this.props.onSubmit}
                     isLast={this.props.isLast}
                     isFirst={this.props.isFirst}
                     error={this.props.errors.error}    
            />

          </div>
        </Fragment>
      )
    }
  }

FirstStep.js

import Steps from './Steps';

export default class FirstStep extends Component {

    constructor(props){
        super(props)
        this.state = {}
    }

    render() {
        //console.log(this.props)
        return (
            <Fragment>
                <Steps level={this.props.index}/>
                <form onSubmit={this.onSubmit} className="mt90">
                    <div>
                        <label>Nombre pleto</label>
                        <input type="text"
                               name="nombre"
                               placeholder="Escriba su nombre"
                               onChange={ event => this.props.onChangeText('nombre', event.target.value) }
                               value={this.props.values.nombre}
                               className={ `${ this.props.errors.errorNombre && "error-focus" }` }
                               />
                        { this.props.errors.errorNombre &&
                        <span className="error">No es un nombre pleto</span> }
                    </div>
                    <div className="mt50">
                        <label>N° de CUIL</label>
                        <input type="text"
                               name="cuil"
                               placeholder="Ej.: 23-45678901-2"
                               onChange={ event => this.props.onChangeText('cuil', event.target.value) }
                               value={this.props.values.cuil}
                               className={ `${ this.props.errors.errorCuil && "error-focus" }` }
                               />
                        { this.props.errors.errorCuil &&
                        <span className="error">No es un CUIL válido</span> }
                    </div>
                </form>
            </Fragment>
        )
    }
}

App.js (has the FirstStep as a child ponent)

[...]
Render()
<RegisterBox.Step>
            { ({ values, onChangeValue, index, errors }) => (
              <FirstStep values={values} onChangeText={onChangeValue} index={index} errors={errors} />
            )}
          </RegisterBox.Step>
[...]
Share Improve this question edited Aug 25, 2019 at 10:09 Pavindu 3,1238 gold badges52 silver badges89 bronze badges asked Aug 10, 2018 at 6:02 Leandro AranguezLeandro Aranguez 911 silver badge5 bronze badges 2
  • Try moving this.validate(name) outside of your setState method – Adam Johnston Commented Aug 10, 2018 at 7:08
  • That works but is not validating at the same time when you type in the input.. i mean, when i need a 11 lenght name, only when i type 12 times checks that it is Ok, i was trying this to make the change inmediatly because i can't call it in render (infinite loop) – Leandro Aranguez Commented Aug 10, 2018 at 7:27
Add a ment  | 

2 Answers 2

Reset to default 3

Looks like you've made a small typographical error. Your solution should look a bit more like this.

_onChangeValue = (name, value) => {
    this.setState(prevState => {
      return {
        values: {
          ...prevState.values,
          [name]: value
        }
      }
    }, () => this.validate(name));
  };

Notice the call to this.validate has moved out one set of brackets. Secondly, it's been wrapped in an anonymous function, so that it can actually be used as a callback.

I imagine this would have resulted in some confusing behaviour. Because of the ma operator, your setState would not have made any update to values, instead updating state with only the results of your validation!

I think you need to rethink about this problem. To make sure that we are in the same pace, let me reiterate several things.

First of all setState is asynchronous and more like a signal to React to update the state object and invokes functions of React life cycle.

Nested setState is discouraged since it's hard to reason about the behavior of asynchronous calls.

You need to restructure your functions to avoid nested setState. I can't do that for you in this brief answer and you know the best how you want to do so. One thing that you need to notice is that sometimes, you could serialize setState calls to make sure that the prevState you are seeing in this current setState call is actually from the previous call. A way to make it work is to wrap setState with a Promise and use await/async to serialize the calls.

Hope this helps a bit.

发布评论

评论列表(0)

  1. 暂无评论