首页 > 解决方案 > 取消 useEffect 清理函数中的所有订阅和异步任务 - 我做错了什么?

问题描述

我正在尝试根据用户是否登录来更改按钮。该功能由 MyNav.js 管理。如果用户已登录,我会显示退出按钮,否则,我会显示登录和注册按钮。如果您能指导我做错了什么,那就太好了。

警告:

Can't perform a React state update on an unmounted component. 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.
    at LoginSigninModal (http://localhost:3000/static/js/main.chunk.js:530:81)
    at MenuButtons

MyNav.js

import React, { useEffect, useState } from 'react'
import Nav from 'react-bootstrap/Nav'
import Navbar from 'react-bootstrap/Navbar'
// import NavDropdown from 'react-bootstrap/NavDropdown'
import { Link } from 'react-router-dom'
import Searchfield from '../product_search_components/Searchfield'
import LoginSignout from '../login_components/LoginSignout'
import MenuButtons from './MenuButtons'

export default function MyNav(props) {
    var isSigned
    if (localStorage.getItem('scs_user_logged_in')){
        isSigned = true
    }else{
        isSigned = false
    }
    const [signedIn, setSignedIn] = useState(isSigned)
    
    var menu
    if (signedIn) {
        menu = <LoginSignout  setSignedIn={setSignedIn}/>
    } else {
        menu = <MenuButtons setSignedIn={setSignedIn}/>
    }

    useEffect(()=>{
        
    },[signedIn])

    return (
        <Navbar bg="light" expand="lg">
            <Navbar.Brand><span style={{ fontWeight: 'bold' }}>Test</span></Navbar.Brand>

            <Navbar.Toggle aria-controls="basic-navbar-nav" />
            <Navbar.Collapse id="basic-navbar-nav">
                <Nav className="mr-auto">
                    <Link className='nav-link' to="/">Home</Link>
                    <Link className='nav-link' to="/about">About</Link>
                    <Link className='nav-link' to="/todo">Todo</Link>


                    {/* <NavDropdown title="Dropdown" id="basic-nav-dropdown">
                        <NavDropdown.Item>Action</NavDropdown.Item>
                        <NavDropdown.Item>Another action</NavDropdown.Item>
                        <NavDropdown.Item>Something</NavDropdown.Item>
                        <NavDropdown.Divider />
                        <NavDropdown.Item>Separated link</NavDropdown.Item>
                    </NavDropdown> */}
                </Nav>

            </Navbar.Collapse>
            <Searchfield search_item={props.search_item} setSearchItem={props.setSearchItem} />
            {menu}

        </Navbar>
    )
}

菜单按钮.js

import LoginSigninModal from '../login_components/LoginSigninModal'
import LoginSignupModal from '../login_components/LoginSignupModal'


export default function MenuButtons(props) {
    return (
        <>
            <LoginSigninModal {...props}/>
            <LoginSignupModal />
        </>
    )
}

登录SigninModal.js

import React, { useState, useEffect } from 'react'
import Button from 'react-bootstrap/Button'
import Modal from 'react-bootstrap/Modal'
import Form from 'react-bootstrap/Form'
import Alert from 'react-bootstrap/Alert'
import Spinner from 'react-bootstrap/Spinner'
import { baseURL } from './../assets/axiosInstance'
import axios from 'axios'
// import LoginSignupModal from './LoginSignupModal'


export default function LoginSigninModal(props) {
  const [show, setShow] = useState(false);
  const handleClose = () => setShow(false);
  const handleShow = () => setShow(true);
  const [localSigned, setLocalSigned] = useState(false)

  const [email, setEmail] = useState("")
  const [password, setPassword] = useState("")
  const errorEmail = false
  const [apiCalling, setApiCalling] = useState(false)

  const [messageOnSubmit, setMessageOnSubmit] = useState({
    type: "",
    message: ""
  })

  useEffect(() => {
    props.setSignedIn(localSigned)

  }, [localSigned, props])

  function handleSubmit() {
    setApiCalling(true)

    const getData = async () => {
      await axios.post(baseURL + 'auth/login', {
        email: email,
        password: password,
      })
        .then((response) => {
          localStorage.setItem('scs_token', response.data['access_token'])
          localStorage.setItem('scs_user', JSON.stringify(response.data['user']))
          localStorage.setItem('scs_user_logged_in', true)

          setLocalSigned(true)

          setMessageOnSubmit({
            type: "success",
            message: "Login Successful"
          })
        })
        .catch((error) => {
          console.log(error.response.status)
          if(error.response.status === 401){
            setMessageOnSubmit({
              type: "danger",
              message: "Login failed due to wrong password."
            })
          }else if(error.response.status === 422){
            setMessageOnSubmit({
              type: "danger",
              message: "Password needs to be at least 6 characters long."
            })
          }
        })
        .then(()=>{
          setApiCalling(false)
        })
    }

    getData()
    
  }

  return (
    <>
      <Button variant="primary" onClick={handleShow} style={{ marginLeft: '5px' }}>
        Signin
        </Button>

      <Modal
        show={show}
        onHide={handleClose}
        backdrop="static"
        keyboard={false}
      >
        <Modal.Header closeButton>
          <Modal.Title>Signin</Modal.Title>
        </Modal.Header>
        <Modal.Body>
          <Form>
            <Form.Group controlId="formBasicEmail">
              <Form.Label>Email address</Form.Label>
              <Form.Control value={email} onChange={(data) => setEmail(data.target.value)} type="email" placeholder="xyz@email.com" />
            </Form.Group>
            <Form.Group controlId="formBasicPassword">
              <Form.Label>Password</Form.Label>
              <Form.Control value={password} onChange={(data) => setPassword(data.target.value)} type="password" placeholder="Password" />
            </Form.Group>

            {(
              messageOnSubmit['type'] !== ""
                ?
                <Alert variant={messageOnSubmit['type']}>
                  {messageOnSubmit['message']}
                </Alert>
                :
                "")}

            <Button variant="primary" type="button" disabled={errorEmail} active={!errorEmail} onClick={handleSubmit}>Login {apiCalling && <Spinner animation="border" size="sm" />}</Button>
            <Button variant="secondary" onClick={handleClose} style={{ marginLeft: "5px" }}>Close</Button>

          </Form>
        </Modal.Body>
        <Modal.Footer>

        </Modal.Footer>
      </Modal>
    </>
  );
}

http://localhost:3000/static/js/main.chunk.js:530:81 - (来自警告消息)

function LoginSigninModal(props) {
  _s();

  const [show, setShow] = Object(react__WEBPACK_IMPORTED_MODULE_1__["useState"])(--error marked here--)(false);

  const handleClose = () => setShow(false);

  const handleShow = () => setShow(true);

标签: reactjs

解决方案


问题出在 LoginSigninModal.js 的以下代码块中

     .then((response) => {
          localStorage.setItem('scs_token', response.data['access_token'])
          localStorage.setItem('scs_user', JSON.stringify(response.data['user']))
          localStorage.setItem('scs_user_logged_in', true)

          setLocalSigned(true)

          setMessageOnSubmit({
            type: "success",
            message: "Login Successful"
          })
        })

成功登录后,我更改了 localSigned 状态。卸载登录模式组件。然后我尝试更新该组件上的消息。由于组件已卸载,因此出现错误。删除 setMessageonSubmit 或在 setMessageSubmit 后调用 set setLocalSigned 可解决问题。

正确的代码将是:

.then((response) => {
          localStorage.setItem('scs_token', response.data['access_token'])
          localStorage.setItem('scs_user', JSON.stringify(response.data['user']))
          localStorage.setItem('scs_user_logged_in', true)

          setMessageOnSubmit({
            type: "success",
            message: "Login Successful"
          })
          
          setLocalSigned(true)
        })

或者

.then((response) => {
  localStorage.setItem('scs_token', response.data['access_token'])
  localStorage.setItem('scs_user', JSON.stringify(response.data['user']))
  localStorage.setItem('scs_user_logged_in', true)
      
  setLocalSigned(true)
})

推荐阅读