Почему этот вызов API вызывается дважды в React?

avatar
mouchin777
1 июля 2021 в 15:43
761
2
0

Итак, у меня есть следующий компонент

const ChartContainer: React.FC = () => {
  const [data, setData] = React.useState<unknown>([])
  const [prevMonthStamp, setPrevMonthStamp] = React.useState<number>()

  React.useEffect(() => {
    var d: any = new Date();
    d.setMonth(d.getMonth() - 30);//timestamp of x months ago
    d.setHours(0, 0, 0, 0);
    setPrevMonthStamp((d / 1000 | 0) * 1000); 
  }, [])


  React.useEffect(() => {
    let url = binanceApi
    let proxyUrl = corsProxy
    axios({
      method: 'get',
      url: proxyUrl+url
    }).then(res => {
      if (prevMonthStamp) {
        setData(res.data.filter((i: number[]) => i[0] >= prevMonthStamp))
      }
    }).catch(err => {
      console.log(err)
    })
  }, [prevMonthStamp])


  if (Array.isArray(data) && data?.length > 0) {
    return (
      <Canvas data={convertData(data)} />
    );
  } else {
    return <Spinner />
  }

}

И я заметил, что API вызывается дважды

Предположительно, при монтировании компонента я устанавливаю prevMonthStamp, и после его установки используется другой useEffect.

Почему он используется дважды?

Источник
Gulam Hussain
1 июля 2021 в 15:47
2

Ваш второй useEffect запускается дважды: 1 при монтировании и второй раз, когда вы устанавливаете setPrevMonthStamp в свой первый useEffect

Sagar Shakya
1 июля 2021 в 15:48
0

Хук useEffect() вызывается при монтировании компонента. Итак, второй userEffect() выполняется один раз, когда компонент монтируется, и второй раз, когда его зависимость prevMountStamp устанавливается при первом вызове useEffect().

Ответы (2)

avatar
Nicholas Tower
1 июля 2021 в 15:48
2

Вы выполняете рендеринг один раз с prevMonthStamp равным undefined. В результате этого рендеринга запускаются два эффекта, в том числе один для отправки запроса API. Затем вы снова выполняете рендеринг, теперь с prevMonthStamp, равным некоторому числу. Для этого второго рендеринга ваш запрос API выполняется снова.

Исправление состоит в том, чтобы избавиться от двойного рендеринга. Вычислить начальное состояние prevMonthStamp при самом первом рендеринге, а не в эффекте после рендеринга.

  const [prevMonthStamp, setPrevMonthStamp] = React.useState<number>(() => {
    const d: Date = new Date();
    d.setMonth(d.getMonth() - 30);//timestamp of x months ago
    d.setHours(0, 0, 0, 0);
    return (d / 1000 | 0) * 1000
  })
avatar
Afaq Rashid
1 июля 2021 в 15:54
0

Этот второй useEffect (тот, у которого есть зависимость "prevMonthStamp") вызывается дважды. Сначала, когда prevMonthStamp не определен. Второй при обновлении prevMonthStamp.

Вы можете сделать это одним вызовом, присвоив значение prevMonthStamp State при первом рендеринге.

const [prevMonthStamp, setPrevMonthStamp] = React.useState<number>(() =>{
....
return (d / 1000 | 0) * 1000;
})