React Hooks: useEffect () вызывается дважды, даже если в качестве аргумента используется пустой массив - PullRequest
1 голос
/ 10 марта 2020


Я новичок в reactJS и пишу код таким образом, чтобы перед загрузкой данных из БД отображалось сообщение о загрузке, а затем после загрузки отображать компоненты с загруженными данными. Для этого я использую как ловушку useState, так и ловушку useEffect. Вот код:

Проблема в том, что useEffect запускается дважды, когда я проверяю файл console.log. Таким образом, код запрашивает одни и те же данные дважды, чего следует избегать.

Ниже приведен код, который я написал:

import React from 'react';
import './App.css';
import {useState,useEffect} from 'react';
import Postspreview from '../components/Postspreview'

const indexarray=[]; //The array to which the fetched data will be pushed

function Home() {
   const [isLoading,setLoad]=useState(true);
   useEffect(()=>{
      /*
      Query logic to query from DB and push to indexarray
      */
          setLoad(false);  // To indicate that the loading is complete
    })
   },[]);
   if (isLoading===true){
       console.log("Loading");
       return <div>This is loading...</div>
   }
   else {
       console.log("Loaded!"); //This is actually logged twice.
       return (
          <div>
             <div className="posts_preview_columns">
             {indexarray.map(indexarray=>
             <Postspreview
                username={indexarray.username}
                idThumbnail={indexarray.profile_thumbnail}
                nickname={indexarray.nickname}
                postThumbnail={indexarray.photolink}
             />
             )}
            </div>
         </div>  
         );
    }
}

export default Home;

Может кто-нибудь помочь мне понять, почему он вызывается дважды и как правильно исправить код? Большое спасибо!

Ответы [ 2 ]

3 голосов
/ 10 марта 2020

Поместите console.log внутри useEffect

Возможно, у вас есть другие побочные эффекты, которые вызывают повторный рендеринг компонента, но сам useEffect будет вызываться только один раз. Вы можете убедиться в этом с помощью следующего кода:

useEffect(()=>{
      /*
      Query logic to query from DB and push to indexarray
      */
      console.log('i fire once')
      setLoad(false);  // To indicate that the loading is complete
    })
},[]);

Если журнал «Я стреляю один раз» запускается более одного раза, это означает, что ваша проблема выше по дереву. Компонент вынужден размонтировать и перемонтировать при первоначальном рендеринге.

1 голос
/ 10 марта 2020

Не уверен, почему вы не будете переводить результат в состояние, вот пример, который вызывает эффект один раз, поэтому вы, должно быть, сделали что-то в коде, который не был опубликован, чтобы он снова отображался:

const App = () => {
  const [isLoading, setLoad] = React.useState(true)
  const [data, setData] = React.useState([])
  React.useEffect(() => {
    console.log('in effect')
    fetch('https://jsonplaceholder.typicode.com/todos')
      .then(result => result.json())
      .then(data => {
        setLoad(false)//causes re render
        setData(data)//causes re render
      })
  },[])
  //first log in console, effect happens after render
  console.log('rendering:', data.length, isLoading)
  return {JSON.stringify(data, undefined, 2)}
} // сделать приложение ReactDOM.render ( , document.getElementById ('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Чтобы предотвратить дополнительный рендер, вы можете объединить данные и загрузку в одном состоянии:

const useIsMounted = () => {
  const isMounted = React.useRef(false);
  React.useEffect(() => {
    isMounted.current = true;
    return () => isMounted.current = false;
  }, []);
  return isMounted;
};


const App = () => {
  const [result, setResult] = React.useState({
    loading: true,
    data: []
  })
  const isMounted = useIsMounted();
  React.useEffect(() => {
    console.log('in effect')
    fetch('https://jsonplaceholder.typicode.com/todos')
      .then(result => result.json())
      .then(data => {
        //before setting state in async function you should
        //  alsways check if the component is still mounted or
        //  react will spit out warnings
        isMounted.current && setResult({ loading: false, data })
      })
  },[isMounted])
  console.log(
    'rendering:',
    result.data.length,
    result.loading
  )
  return (
    {JSON.stringify(result.data, undefined, 2)}
)} // визуализация приложения ReactDOM.render ( , document.getElementById ('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...