useEffect вызывается 2 раза при рендере несмотря на пустой массив зависимостей
const backUrl = "https://626d63f2e58c6fabe2d4dc9a.mockapi.io/items"
function Main() {
const [items, setItems] = useState([])
useEffect(() => {
async function fetchItems() {
const response = await fetch(backUrl);
const data = await response.json();
console.log("EffectUsed")
setItems(data)
}
fetchItems()
}, []);
}
При этом при рендере а также перерендере хук срабатывает по 2 раза. Уже не знаю что делать
Ответы (2 шт):
StrictMode выполняет рендеринг компонентов дважды в development режиме, но не в production. По мнению разработчиков React - это позволяет обнаружить некоторые проблемы в вашем коде, если таковые будут и предупредить Вас об этом
Более подробно тут Раздел Обнаружение неожиданных побочных эффектов
Очень похоже на баг реакта. А может и не баг (источник):
Stricter Strict Mode: In the future, React will provide a feature that lets components preserve state between unmounts. To prepare for it, React 18 introduces a new development-only check to Strict Mode. React will automatically unmount and remount every component, whenever a component mounts for the first time, restoring the previous state on the second mount. If this breaks your app, consider removing Strict Mode until you can fix the components to be resilient to remounting with existing state.
Если вместо нового рендеринга через createRoot использовать старый через render, то эффенкт будет вызываться 1 раз как и должен. Это касается как 17го, таки 18го реакта, но 18й выдаёт предупреждение о том, что старый рендер устарел.
const { StrictMode, useState, useEffect } = React
const { createRoot } = ReactDOM
const { log } = console
const rootElement = document.querySelector("main");
const root = createRoot(rootElement);
function App() {
log("render")
useState(() => log("useState"))
useEffect(() => log("useEffect"), [])
return <h1>Hi!</h1>
}
root.render(
<StrictMode>
<App />
</StrictMode>
);
<script crossorigin src="//unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="//unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<main></main>
const { StrictMode, useState, useEffect } = React
const { render } = ReactDOM
const { log } = console
function App() {
log("render")
useState(() => log("useState"))
useEffect(() => log("useEffect"), [])
return <h1>Hi!</h1>
}
render(
<StrictMode>
<App />
</StrictMode>,
document.querySelector("main")
);
<script crossorigin src="//unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="//unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<main></main>
В production-сборке в любом случае будет только один вызов (как и один рендеринг):
const { StrictMode, useState, useEffect } = React
const { createRoot } = ReactDOM
const { log } = console
const rootElement = document.querySelector("main");
const root = createRoot(rootElement);
function App() {
log("render")
useState(() => log("useState"))
useEffect(() => log("useEffect"), [])
return <h1>Hi!</h1>
}
root.render(
<StrictMode>
<App />
</StrictMode>
);
<script crossorigin src="//unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="//unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<main></main>
const { StrictMode, useState, useEffect } = React
const { render } = ReactDOM
const { log } = console
function App() {
log("render")
useState(() => log("useState"))
useEffect(() => log("useEffect"), [])
return <h1>Hi!</h1>
}
render(
<StrictMode>
<App />
</StrictMode>,
document.querySelector("main")
);
<script crossorigin src="//unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="//unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<main></main>