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

javascript - Fix "Can't perform a React state update on an unmounted component" error - Stack Overflow

programmeradmin3浏览0评论

The code below gives a console error when I try to push to a new page in my web application:

Warning: Can't perform a React state update on an unmounted ponent. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function. in PasswordResetPage (created by Context.Consumer)

You will have noticed in the code that is mented out that I've tried to use useEffect as this is what most stackoverflow answers points to. However, the error is still present. How can I use history.push in this context and get rid of the error?

import React, { useState /* , useEffect */ } from 'react'
import useForm from 'react-hook-form'
import Logo from '../assets/images/logo-icon-orange.png'
import TextInput from '../ponents/TextInput'
import Footer from '../ponents/Footer'
import AnimatedButton from '../ponents/AnimatedButton'
import logger from '../logger'
import { sleep } from '../utils/promise'
import PasswordResetSchema from './validation/PasswordResetSchema'

const PasswordResetPage = ({ history }) => {
  const [isSubmitting, setSubmitting] = useState(false)
  // const [isDone, setDone] = useState(false)
  const { register, handleSubmit, errors } = useForm({
    validationSchema: PasswordResetSchema,
  })

  const onSubmit = async data => {
    setSubmitting(true)
    await sleep(2000)
    logger.info('form data', data)
    // setDone(true)
    history.push('/confirmation')
  }

  // useEffect(() => {
  //   if (isDone) {
  //     history.push('/confirmation')
  //   }
  // })


  return (
    <form onSubmit={handleSubmit(onSubmit)} noValidate>
      <div className="text-center mb-4">
        <img className="mb-4" src={Logo} alt="Striver" width={72} />
        <h1 className="h3 mb-3 font-weight-normal">Password reset</h1>
        <p>Enter your email address below and we will send you instructions on how you can reset your password.</p>
      </div>

      <div className="form-label-group">
        <TextInput type="email" id="email" register={register} label="Email address" errors={errors} />
      </div>

      <AnimatedButton actionTitle="Next" isSubmitting={isSubmitting} />
      <Footer />
    </form>
  )
}

export default PasswordResetPage

The code below gives a console error when I try to push to a new page in my web application:

Warning: Can't perform a React state update on an unmounted ponent. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function. in PasswordResetPage (created by Context.Consumer)

You will have noticed in the code that is mented out that I've tried to use useEffect as this is what most stackoverflow answers points to. However, the error is still present. How can I use history.push in this context and get rid of the error?

import React, { useState /* , useEffect */ } from 'react'
import useForm from 'react-hook-form'
import Logo from '../assets/images/logo-icon-orange.png'
import TextInput from '../ponents/TextInput'
import Footer from '../ponents/Footer'
import AnimatedButton from '../ponents/AnimatedButton'
import logger from '../logger'
import { sleep } from '../utils/promise'
import PasswordResetSchema from './validation/PasswordResetSchema'

const PasswordResetPage = ({ history }) => {
  const [isSubmitting, setSubmitting] = useState(false)
  // const [isDone, setDone] = useState(false)
  const { register, handleSubmit, errors } = useForm({
    validationSchema: PasswordResetSchema,
  })

  const onSubmit = async data => {
    setSubmitting(true)
    await sleep(2000)
    logger.info('form data', data)
    // setDone(true)
    history.push('/confirmation')
  }

  // useEffect(() => {
  //   if (isDone) {
  //     history.push('/confirmation')
  //   }
  // })


  return (
    <form onSubmit={handleSubmit(onSubmit)} noValidate>
      <div className="text-center mb-4">
        <img className="mb-4" src={Logo} alt="Striver" width={72} />
        <h1 className="h3 mb-3 font-weight-normal">Password reset</h1>
        <p>Enter your email address below and we will send you instructions on how you can reset your password.</p>
      </div>

      <div className="form-label-group">
        <TextInput type="email" id="email" register={register} label="Email address" errors={errors} />
      </div>

      <AnimatedButton actionTitle="Next" isSubmitting={isSubmitting} />
      <Footer />
    </form>
  )
}

export default PasswordResetPage
Share Improve this question asked Jun 15, 2019 at 6:51 Andre GalloAndre Gallo 2,2695 gold badges24 silver badges46 bronze badges 1
  • Possible duplicate of Can't perform a React state update on an unmounted ponent? – Vahid Al Commented Jun 15, 2019 at 9:25
Add a ment  | 

2 Answers 2

Reset to default 6

See if that works for you:

Code SandBox with working example:

https://codesandbox.io/s/reactrouteranswerso-817bp

const onSubmit = async data => {
    setSubmitting(true)
    await sleep(2000)
    logger.info('form data', data)
    setDone(true)
  }

  useEffect(() => {
    if (isDone) {
      history.push('/confirmation')
    }
  },[isDone]);

Working code on CodeSandbox:

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";

import "./styles.css";

function App() {
  const [user, setUser] = useState(null);

  function doLogin() {
    console.log("Inside do login...");
    setUser("someUserID");
  }

  function doLogout() {
    console.log("Inside do logout...");
    setUser(null);
  }

  return (
    <Router>
      <AllRoutes user={user} doLogin={doLogin} doLogout={doLogout} />
    </Router>
  );
}

function AllRoutes(props) {
  console.log("Rendergin AllRoutes...");
  // console.log(props);
  return (
    <Switch>
      <Route exact path="/" ponent={Component1} />
      <Route exact path="/p2" ponent={Component2} />
    </Switch>
  );
}

function Component1(props) {
  const [isDone, setIsDone] = useState(false);

  useEffect(() => {
    if (isDone) {
      props.history.push("/p2");
    }
  }, [isDone, props.history]);

  return (
    <React.Fragment>
      <div>Component1</div>
      <button onClick={() => setIsDone(true)}>Submit</button>
    </React.Fragment>
  );
}

function Component2(props) {
  const [goBack, setGoBack] = useState(false);

  useEffect(() => {
    if (goBack) {
      props.history.push("/");
    }
  }, [goBack, props.history]);

  return (
    <React.Fragment>
      <div>Component2</div>
      <button onClick={() => setGoBack(true)}>Go Back</button>
    </React.Fragment>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

I believe await sleep(2000) is a replacement for actual API call.
API calls as well as history.push are side effects thus should be places in useEffect.
Try this:

const PasswordResetPage = ({ history }) => {
  const [formData, setFormData] = useState(null);

  // ...

  const onSubmit = async data => {
    setFormData(data);
  }

  useEffect(() => {
    logger.info('form data', formData);
    sleep(2000).then(() => history.push('/confirmation'););
  }, [formData]);

  // ...
}
发布评论

评论列表(0)

  1. 暂无评论