Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
298 views
in Technique[技术] by (71.8m points)

reactjs - Cancel all subscriptions and asynchronous tasks in a useEffect cleanup function - What am I doing wrong?

I am trying to change the button based on if a user is logged in or not. the functionality is managed from MyNav.js. if the user is logged in I show the sign-out button, otherwise, I show the sign-in and sign-up button. It would be great if you can guide what part I am doing wrong.

Warning:

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>
    )
}

MenuButtons.js

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


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

LoginSigninModal.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="[email protected]" />
            </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 - (from the warning message)

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);
question from:https://stackoverflow.com/questions/66064885/cancel-all-subscriptions-and-asynchronous-tasks-in-a-useeffect-cleanup-function

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The issue was in the following code block of 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"
          })
        })

When I successfully logged in, I change localSigned state. which unmounts the login modal component. I then try to update the message on that component. as the component is already unmounted, was getting the error. removing the setMessageonSubmit or calling set setLocalSigned after the setMessageSubmit, fixes the issue.

Correct code will be:

.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)
        })

or

.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)
})

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...