简体   繁体   English

如何修复无法在未安装的组件上执行 React state 更新

[英]How to fix Can't perform a React state update on an unmounted component

I have a simple React components that fetches data from the database using Axios, it works but when I open the console I notice the following error:我有一个简单的 React 组件,它使用 Axios 从数据库中获取数据,它可以工作,但是当我打开控制台时,我注意到以下错误:

index.js:1375 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 the componentWillUnmount method.

As far I read to fix it you need to use componentDidMount with a conditional statement, but I don't know how to apply that using the useEffect hook.据我阅读要修复它,您需要使用带有条件语句的 componentDidMount,但我不知道如何使用useEffect挂钩来应用它。 How can I fix the error?如何修复错误? Here is my attempt:这是我的尝试:

import React, { useState, useEffect, useRef } from 'react';
import axios from 'axios';
import { Link } from 'react-router-dom';
import SecondColumn from '../SecondColumn/SecondColumn';
import Text from '../Text/Text';
import Pagination from "react-pagination-js";
import Spinner from '../Spinner/Spinner';

const Posts = () => {

  let [posts, setPosts] = useState([]);
  const [currentPage, setCurrentPage] = useState(1);
  const [loading, setLoading] = useState(false);
  const [postsPerPage] = useState(5);
  const isMounted = useRef(false)
  const GET_POSTS_API = process.env.REACT_APP_GET_POSTS_API;

  useEffect(() => {
    isMounted.current = true;
    const fetchPosts = async () => {
      setLoading(true);
      if (isMounted.current) {
        let res = await axios.get(GET_POSTS_API);
        setPosts(res.data);
        setLoading(false);
      }

    };

    fetchPosts();
    return () => { isMounted.current = false }
  }, []);

  // Get current posts
  const indexOfLastPost = currentPage * postsPerPage;
  const indexOfFirstPost = indexOfLastPost - postsPerPage;
  const currentPosts = posts.slice(indexOfFirstPost, indexOfLastPost);

  if (loading) {
    return <Spinner />
  }

  // Change page
  const paginate = (pageNumber) => {
    if (pageNumber > 0) {
      setCurrentPage(pageNumber);
    }
  }

  return (
    <div className="row">
      <div className="column">
        {currentPosts.map(post => (
          <div key={post._id} className='post'>
            <img className="post-container__image" src={post.picture} alt="avatar" />
            <div className="post-container__post">
              <div className="post-container__text">
                <h2 className="post-container__title">{post.title}</h2>
                <p className="post-container__date">{post.date}</p>
                <p className="post-info-container__text">{post.postContent.substring(0, 310) + "..."}</p>
                <Link to={`/post/${post._id}`} className="read-more-btn">
                  <button className="read-more-btn">Read more</button>
                </Link>
              </div>
            </div>
          </div>
        ))}
        <Pagination
          currentPage={currentPage}
          currentPosts={currentPosts}
          showFirstLastPages={true}
          sizePerPage={postsPerPage}
          totalSize={posts.length}
          totalPages={posts.length}
          changeCurrentPage={paginate}
        />
        <Text />
      </div>
      <div className="column">
        <SecondColumn />
      </div>
    </div>
  )
};

export default Posts;

After applying all solutions posted here, none seem to remove the error I debugged more and it turns out that in the SecondColumn component I have a component FacebookProvider coming from the react-facebook package.应用此处发布的所有解决方案后,似乎没有一个可以消除我调试的错误,事实证明,在 SecondColumn 组件中,我有一个来自 react-facebook package 的组件 FacebookProvider。 When I remove the component I don't get this error, but I need to have the component on the page, any idea why this package is causing this error and how to fix it?当我删除组件时,我没有收到此错误,但我需要在页面上显示该组件,知道为什么此 package 会导致此错误以及如何修复它? here is the full code of the SecondColumn where the FacebookProvider is located:这是 FacebookProvider 所在的 SecondColumn 的完整代码:

import React from 'react';
import titles from '../../enums.js'
import { FacebookProvider, Page } from 'react-facebook';
import CustomHeading from '../CustomHeading/CustomHeading.jsx'


class SecondColumn extends React.Component {

  render() {
    return <div className="second-column">

      <div className="second-column__about-me">
        <CustomHeading text={titles.ABOUT_ME} className="astro-heading-margin-left" />
        <img className="second-column__first_part__test-img-col" src={require("../../assets/images/fake-image.png")} alt="fake img for now" />
        <div className="second-column_first_part_text-parent">
          <p className="second-column_first_part_text-parent__text-body">Lorem ipsum dolor, sit amet consectetur adipisicing elit.
          Molestiae molestias tempora ratione dolorum, tenetur laborum blanditiis id  Molestiae molestias tempora ratione dolorum, tenetur laborum blanditiis id
          Molestiae molestias tempora ratione dolorum, tenetur laborum blanditiis id  Molestiae molestias tempora ratione dolorum, tenetur laborum blanditiis id
          Molestiae molestias tempora ratione dolorum, tenetur laborum blanditiis id  Molestiae molestias tempora ratione dolorum, tenetur laborum blanditiis id
           </p>
        </div>
      </div>

      <div className="second-column__first_part">
        <CustomHeading text={titles.MY_ASTROLOGICAL_READINGS} className="astro-heading-margin-left" />

        <img className="second-column__first_part__test-img-col" src={require("../../assets/images/fake-image.png")} alt="fake img for now" />
        <img className="second-column__first_part__test-img-col" src={require("../../assets/images/fake-image.png")} alt="fake img for now" />
        <img className="second-column__first_part__test-img-col" src={require("../../assets/images/fake-image.png")} alt="fake img for now" />
        <button className="btn">click here for all readings</button>
      </div>
      <div className="second-column__second_part">
        <h3 className="second-column__second_part__title"> Signs in a nutshell</h3>
        <div className="second-column__second_part__container">
          <div className="item">
            <img className="item__astro-icon" src={require("../../assets/astro-signs/002-aries.png")} alt="aries sign" />
            <p className="title">Aries</p>
          </div>

          <div className="item">
            <img className="item__astro-icon" src={require("../../assets/astro-signs/022-taurus.png")} alt="taurus sign" />
            <p className="title">Taurus</p>
          </div>

          <div className="item">
            <img className="item__astro-icon" src={require("../../assets/astro-signs/007-gemini.png")} alt="taurus sign" />
            <p className="title">Gemini</p>
          </div>
          <div className="item">
            <img className="item__astro-icon" src={require("../../assets/astro-signs/003-cancer.png")} alt="cancer sign" />
            <p className="title">Cancer</p>
          </div>
          <div className="item">
            <img className="item__astro-icon" src={require("../../assets/astro-signs/009-leo.png")} alt="leo sign" />
            <p className="title">Leo</p>
          </div>

          <div className="item">
            <img className="item__astro-icon" src={require("../../assets/astro-signs/025-virgo.png")} alt="virgo sign" />
            <p className="title">Virgo</p>
          </div>
          <div className="item">
            <img className="item__astro-icon" src={require("../../assets/astro-signs/010-libra.png")} alt="libra sign" />
            <p className="title">Libra</p>
          </div>
          <div className="item">
            <img className="item__astro-icon" src={require("../../assets/astro-signs/020-scorpio.png")} alt="scorpio sign" />
            <p className="title">Scorpio</p>
          </div>
          <div className="item">
            <img className="item__astro-icon" src={require("../../assets/astro-signs/018-sagittarius.png")} alt="sagitarius sign" />
            <p className="title">Sagitarius</p>
          </div>
          <div className="item">
            <img className="item__astro-icon" src={require("../../assets/astro-signs/004-capricorn.png")} alt="capricorn sign" />
            <p className="title">Capricorn</p>
          </div>
          <div className="item">
            <img className="item__astro-icon" src={require("../../assets/astro-signs/001-aquarius.png")} alt="aquarius sign" />
            <p className="title">Aquarius</p>
          </div>
          <div className="item">
            <img className="item__astro-icon" src={require("../../assets/astro-signs/016-pisces.png")} alt="pisces sign" />
            <p className="title">Pisces</p>
          </div>
        </div>
      </div>
      <div className="second-column__third_part">
        <CustomHeading text={titles.DISCOVER_MORE_TOPICS} className="astro-heading-no-margin" />
        <div className="second-column__third_part-item">
          <div className="second-column__third-part-item__text">
            <p className="second-column__third-part-item__text_cont_title">Planets in astrology</p>
            <p className="second-column__third-part-item__text-cont">Lorem ipsum dolor, sit amet consectetur adipisicing elit.
            Molestiae molestias tempora ratione dolorum, tenetur laborum blanditiis id

           </p>
          </div>
        </div>
        <div className="second-column__third_part-item">
          <div className="second-column__third-part-item__text">
            <p className="second-column__third-part-item__text_cont_title">Houses in Astrology</p>
            <p className="second-column__third-part-item__text-cont">Lorem ipsum dolor, sit amet consectetur adipisicing elit.
            Molestiae molestias tempora ratione dolorum, tenetur laborum blanditiis id
          </p>
          </div>
        </div>
        <div className="second-column__third_part-item">
          <div className="second-column__third-part-item__text_cont">
            <p className="second-column__third-part-item__text_cont_title">Astrology Aspects</p>
            <p className="second-column__third-part-item__text-cont">Lorem ipsum dolor, sit amet consectetur adipisicing elit.
            Molestiae molestias tempora ratione dolorum, tenetur laborum blanditiis id
            </p>
          </div>
        </div>

      </div>
      <div className="second-column__fourth_part">
        <CustomHeading text={titles.CONNECT_WITH_ME_ON_FACEBOOK} className="astro-heading-no-margin" />
        <FacebookProvider appId="156975948863691">
          <Page href="https://www.facebook.com/UserName" tabs="timeline" />
        </FacebookProvider>
      </div>
    </div>
  }

};

export default SecondColumn;

In order for your variables to be persisted across renders, you need to define them using useRef为了使您的变量在渲染中保持不变,您需要使用 useRef 定义它们

Also the isMounted variable can be updated using an useEffect cleanup function.也可以使用 useEffect 清理 function 更新 isMounted 变量。

However since you are asynchronously updating state only in useEffect, you can simply define a variable in useEffect但是,由于您仅在 useEffect 中异步更新 state,您可以简单地在 useEffect 中定义一个变量

      useEffect(() => {
        const isMounted = true;
        const fetchPosts = async () => {
          setLoading(true);
          if (isMounted) {
            let res = await axios.get(GET_POSTS_API);
            setPosts(res.data);
          }
          setLoading(false);
        };

        fetchPosts();
        // cleanup function
        return () => { isMounted = false; }
      }, []);

isMounted doesn't need to be state, it can just be a reference: isMounted不需要是 state,它可以只是一个参考:

const isMounted = useRef(false);

Then in your useEffect, you can set it to true:然后在您的 useEffect 中,您可以将其设置为 true:

useEffect(() => {
    isMounted.current = true;

Your useEffect function can also return a function that will be called before unMounting your component, this is where you can set your isMounted back to false:您的 useEffect function 也可以返回一个 function 在卸载组件之前将被调用,这是您可以将 isMounted 设置回 false 的地方:

useEffect(() => {
    isMounted.ref = true;
    ...
    return {() => { isMounted.current = false }
}, []);

To test if your component is mounted, you do:要测试您的组件是否已安装,请执行以下操作:

if (isMounted.current) ...

Below is the example to prevent:下面是防止的例子:

  useEffect(() =&gt; {
let isCancelled = false;
const runAsync = async () =&gt; {
  try {
    if (!isCancelled) {
      // do the job
    }
  } catch (e) {
    if (!isCancelled) {
      throw e;
    }
  }
};

runAsync();

return () =&gt; {
  isCancelled = true;
};

}, [...]); }, [...]);

Firstly, you don't need isMounted , because your useEffect hook only runs once due to the empty dependencies array (it is similar to componentDidMount ).首先,您不需要isMounted ,因为您的useEffect挂钩仅运行一次,因为依赖项数组为空(类似于componentDidMount )。

This error tells you that you need to clean up your fetch call in useEffect .此错误告诉您需要清理useEffect中的 fetch 调用。

I have two methods:我有两种方法:

  1. Simple : use a token/flag to skip the state update when the component unmounts.简单:当组件卸载时,使用令牌/标志跳过 state 更新。
useEffect(() => {
  const isCanceled = false;

  const fetchPosts = async () => {
    setLoading(true);
    let res = await axios.get(GET_POSTS_API);
    if (!isCanceled) {
      setPosts(res.data);
      setLoading(false);
    }
  };

  fetchPosts();

  return () => {
    isCanceled = true;
  };
}, []);
  1. Recommended : Cancel your request With axios, you can use CancelToken .推荐:取消您的请求 使用 axios,您可以使用CancelToken (Read more here: https://github.com/axios/axios#cancellation ) (在这里阅读更多: https://github.com/axios/axios#cancellation
useEffect(() => {
  const source = axios.CancelToken.source();

  const fetchPosts = async () => {
    setLoading(true);

    try {
      let res = await axios.get(GET_POSTS_API, {
        cancelToken: source.token
      });
      setPosts(res.data);
      setLoading(false);
    } catch (error) {
      if (axios.isCancel(error)) {
        //cancelled
      } else {
        throw error;
      }
    }
  };

  fetchPosts();

  return () => {
    source.cancel();
  };
}, []);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 修复“无法对卸载的组件执行 React 状态更新”错误 - Fix "Can't perform a React state update on an unmounted component" error 如何修复“无法在未安装的组件上执行React状态更新。” React Native中的错误 - How to fix “Can't perform a React state update on an unmounted component.” error in React Native 如何解决“无法对未安装的组件执行反应状态更新” - How to solve "can't perform a react state update on an unmounted component" 如何修复:警告:无法在未安装的组件上执行 React state 更新 - How to fix: Warning: Can't perform a React state update on an unmounted component React 警告:无法对卸载的组件执行 React 状态更新。 要修复,请取消所有订阅 - React Warning: Can't perform a React state update on an unmounted component. To fix, cancel all subscriptions React - 无法在未安装的组件上执行 React state 更新 - React - Can't perform a React state update on an unmounted component React Can't perform a React state update on an unmounted component error - React Can't perform a React state update on an unmounted component error React Native:无法对未安装的组件执行 React state 更新 - React Native: Can't perform a React state update on an unmounted component React:无法在未安装的组件上执行 React state 更新 - React: Can't perform a React state update on an unmounted component 导航问题 - 无法对卸载的组件执行 React 状态更新 - Navigation issue - can't perform a React state update on unmounted component
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM