React асинхронная загрузка данных в useEffect
Пытаюсь загрузить данные через асинхронный метод в useEffect. Передаю все необходимые зависимости и в моем понимании useEffect должен отрабатывать при монтировании компонента, при первом рендере и при изменении зависимостей.
useEffect(() => {
console.log('effect')
if (ids.length === 0) {
api.images.all().then((data) => { console.log(data); setIDs(data) }).catch(console.log)
}
}, [ids])
В моем случае это 3 раза: монтирование (тут же он должен загрузить данные), первый рендер (не должно войти в if), и из-за изменения ids (также не должно войти в if). Но useEffect срабатывает 4 раза и загружает данные дважды, я не могу понять почему.
Полный код компонента:
//BuildIn
import { useEffect, useState } from 'react'
//Inside
import api from '../services/api.service'
import AsyncImage from '../components/AsyncImage.component'
const ImagesPage = () => {
const [ids, setIDs] = useState([])
useEffect(() => {
console.log('effect')
if (ids.length === 0) {
api.images.all().then((data) => { console.log(data); setIDs(data) }).catch(console.log)
}
}, [ids])
return(
<>
{(ids.length > 0) ? ids.map((id, index) => <AsyncImage guid={id} key={index} />) : <div>No data</div>}
</>
)
}
export default ImagesPage
Ответы (1 шт):
Первая причина почему так происходит.
Ваш компонент используется в нескольких местах.
Это довольно логично, если компонент в нескольких местах, то соответственно эффект будет вызываться из-за переиспользования компонента. И запрос будет отправляться каждый раз.
Вторая причина.
Что-то наверху в дереве компонентов обновляется.
Очень вероятно что один или несколько родителей обновляются. Или просто монтируются и размонтируются тоже может вызвать перерисовку этого компонента в дереве потомков. Надо просто по всем родительским компонентам пройтись и посмотреть что вызывает ререндер.
Третья причина.
Включен строгий режим реакта (React.StrictMode)
Я лично склоняюсь к третьей причине. Скорее всего у вас просто стоит строгий режим. Так как вы работаете в режиме разработки.
Приведу пример:
App.js
export default function App() {
const [todos, setTodos] = useState([]);
useEffect(() => {
if (todos.length === 0) {
console.log("Внутри");
fetch("https://jsonplaceholder.typicode.com/todos/1")
.then((response) => response.json())
.then((response) => setTodos(response))
.then(() => console.log("последний then"));
}
console.log("эффект", todos);
}, [todos]);
return <div className="App"></div>;
}
index.js
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App";
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
root.render(
<StrictMode>
<App />
</StrictMode>
);
Обычный компонент. Со строгим режимом будет два раза заходить в блок if
if (todos.length === 0) {
Да, именно. Если включен StrictMode он ДВА раза будет заходить и дважды отправит запрос. Можно проверить тут.
Это примерно такая же логика как у вас. Если уберете StrictMode, то все будет работать как вы ожидали.
import { createRoot } from "react-dom/client";
import App from "./App";
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
root.render(<App />);
