Cannot flush updates when React is already rendering

JavascriptReactjsSweetalert

Javascript Problem Overview


I'm trying to show an alert when the API returns an error. For the alert window I'm using sweetalert2. In my render method I'm checking if the error message contains content. If it contains an error message I want to show the user an alert.

When I submit the form, I make an API call. If it returns an error the reducer changes the store (state) and it renders the page again.

Since I added the line below, I keep getting an error:

{saveLabelFetchError && this.toggleAlertFailure(saveLabelFetchError)}

error:

> index.js:1375 Warning: unstable_flushDiscreteUpdates: Cannot flush updates > when React is already rendering.

My component:

import React, { Component } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { bindActionCreators } from "redux";

import { saveLabelValueRequest } from "../../actions/labels";

import Swal from "sweetalert2";
import "./styles.css";
import Button from "@kof/button";

export class NewLabels extends Component {
  state = {
    labelInput: ""
  };

  inputChangedhandler = e => {
    this.setState({ labelInput: e.target.value });
  };

  toggleAlertFailure = message => {
    Swal.fire({
      type: "error",
      title: "Oops...",
      text: message
    });
  };

  saveLabel = event => {
    event.persist();
    event.preventDefault();
    Swal.fire({
      title: "Are you sure?",
      text: "You won't be able to revert this!",
      type: "warning",
      showCancelButton: true,
      confirmButtonColor: "#3085d6",
      cancelButtonColor: "#d33",
      confirmButtonText: "Yes, save it."
    }).then(result => {
      if (result.value) {
        const labelKeyUuid = this.props.labelKey.uuid;
        const labels = event.target.elements.labels.value;
        this.props.saveLabelValue(labelKeyUuid, labels);
      }
    });
  };

  render() {
    const { load, saveLabelFetchError } = this.props;
    return (
      <div>
        <form onSubmit={this.saveLabel}>
          <textarea onChange={this.inputChangedhandler}></textarea>
          <textarea></textarea>
          <Button onClick={() => load(this.state.labelInput)}>Preview</Button>
          <Button type="submit">Save</Button>
        </form>
        {saveLabelFetchError && this.toggleAlertFailure(saveLabelFetchError)}
      </div>
    );
  }
}

NewLabels.propTypes = {
  saveLabelFetchError: PropTypes.string,
  isFetching: PropTypes.bool,
  labelKey: PropTypes.object,
  saveLabelValue: PropTypes.func
};

NewLabels.defaultProps = {
  saveLabelFetchError: "",
  labelKey: {},
  isFetching: false,
  saveLabelValue: () => {}
};

export default connect(
  state => ({
    saveLabelFetchError: state.labelsStore.saveLabelError,
    isFetching: state.labelsStore.isFetching,
    labelKey: state.labelsStore.labelKey
  }),
  dispatch =>
    bindActionCreators(
      {
        saveLabelValue: saveLabelValueRequest
      },
      dispatch
    )
)(NewLabels);

I would like to know why i keep getting this error message in my console.

Javascript Solutions


Solution 1 - Javascript

{saveLabelFetchError && this.toggleAlertFailure(saveLabelFetchError)}

You are trying to update the dom before render cycle i.e before component has mounted. hence, you are getting an error.

Ideally, you should avoid using any library that mutates dom directly (not via react APIs) with react lib. You can read more from here

The solution is to check if there is a change in props value, if so then show error popup. And also make sure we are not mutating dom during react's render cycle.


import React, { Component } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { bindActionCreators } from "redux";

import { saveLabelValueRequest } from "../../actions/labels";

import Swal from "sweetalert2";
import "./styles.css";
import Button from "@kof/button";

export class NewLabels extends Component {
  state = {
    labelInput: "",
    prevSaveLabelFetchError: ""
  };

  static getDerivedStateFromProps(props, state) {
    if (props.saveLabelFetchError !== state.prevSaveLabelFetchError) {
      this.toggleAlertFailure(props.saveLabelFetchError);
      return {
        prevSaveLabelFetchError: props.saveLabelFetchError
      };
    }
    return null;
  }

  inputChangedhandler = e => {
    this.setState({ labelInput: e.target.value });
  };

  toggleAlertFailure = message => {
    Swal.fire({
      type: "error",
      title: "Oops...",
      text: message
    });
  };

  saveLabel = event => {
    event.persist();
    event.preventDefault();
    Swal.fire({
      title: "Are you sure?",
      text: "You won't be able to revert this!",
      type: "warning",
      showCancelButton: true,
      confirmButtonColor: "#3085d6",
      cancelButtonColor: "#d33",
      confirmButtonText: "Yes, save it."
    }).then(result => {
      if (result.value) {
        const labelKeyUuid = this.props.labelKey.uuid;
        const labels = event.target.elements.labels.value;
        this.props.saveLabelValue(labelKeyUuid, labels);
      }
    });
  };

  render() {
    const { load } = this.props;
    return (
      <div>
        <form onSubmit={this.saveLabel}>
          <textarea onChange={this.inputChangedhandler}></textarea>
          <textarea></textarea>
          <Button onClick={() => load(this.state.labelInput)}>Preview</Button>
          <Button type="submit">Save</Button>
        </form>
      </div>
    );
  }
}

NewLabels.propTypes = {
  saveLabelFetchError: PropTypes.string,
  isFetching: PropTypes.bool,
  labelKey: PropTypes.object,
  saveLabelValue: PropTypes.func
};

NewLabels.defaultProps = {
  saveLabelFetchError: "",
  labelKey: {},
  isFetching: false,
  saveLabelValue: () => {}
};

export default connect(
  state => ({
    saveLabelFetchError: state.labelsStore.saveLabelError,
    isFetching: state.labelsStore.isFetching,
    labelKey: state.labelsStore.labelKey
  }),
  dispatch =>
    bindActionCreators(
      {
        saveLabelValue: saveLabelValueRequest
      },
      dispatch
    )
)(NewLabels);


You can also look at react wrapper for sweetalert2

Solution 2 - Javascript

Try this code snippet:

const [state, setState] = useState(true)

const renderFunc = () => {
    ...
    setState(false)
}

state && renderFunc()

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJ. AdamView Question on Stackoverflow
Solution 1 - JavascriptAbhishekView Answer on Stackoverflow
Solution 2 - JavascriptMihail SitnicView Answer on Stackoverflow