3
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 раза. Уже не знаю что делать

Maksim Bogdanov
  • 466
  • 1
  • 3
  • 14
Alternant
  • 43
  • 1
  • 1
  • 6
  • Используете React.StrictMode ? – Maksim Bogdanov Apr 30 '22 at 19:59
  • @Maxim , да, верно. Он изначально был в  index.js, я его решил оставить. Он вызывает проблему? – Alternant Apr 30 '22 at 20:24
  • Благодарю! Убрал и проблема решилась. Очень долго провозился с поиском второго вызова, а оказалось вот в чем проблема. Не могли бы вы подсказать, почему из-за строгого режима такое происходит? – Alternant Apr 30 '22 at 20:27

2 Answers2

8

StrictMode выполняет рендеринг компонентов дважды в development режиме, но не в production. По мнению разработчиков React - это позволяет обнаружить некоторые проблемы в вашем коде, если таковые будут и предупредить Вас об этом

Более подробно тут Раздел Обнаружение неожиданных побочных эффектов

Maksim Bogdanov
  • 466
  • 1
  • 3
  • 14
  • Хм.. Очень похоже на баг. С тем, что должно быть чистыми функциями - логично, а вот побочные эффекты дважды вызывать - это какая-то дичь. Тем более, при старом рендеринге вызывается единожды. – Qwertiy Apr 30 '22 at 21:36
  • В английском сегменте видел где то ссылку на issue по этому поводу особо тогда не вчитывался подробно, но как я понял - не баг, а фича =) – Maksim Bogdanov Apr 30 '22 at 21:42
  • Не похоже на фичу - побочные эффекты многократно вызывать. Это идиотизм, а не фича. Можно ссылку, именно на useEffect? На useState сам нашёл. – Qwertiy Apr 30 '22 at 21:49
  • @Qwertiy Вот тут - в Breaking Changes, пункт Stricter Strict Mode говорится о грядущей новой функции React из за которой изменили поведение строго режима, как я понял этим обусловлен двойной вызов useEffect – Maksim Bogdanov May 01 '22 at 13:53
  • Да, если они перемонтируют компонент, то второй вызов логичен. Но это очень странно... Добавил ссылку в свой ответ. – Qwertiy May 01 '22 at 20:07
5

Очень похоже на баг реакта. А может и не баг (источник):

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>
Qwertiy
  • 123,725