Почему моя функция не может прочитать фактическое значение состояния моего компонента? - PullRequest
0 голосов
/ 18 октября 2019

Для школьного проекта моя группа строит таблицу, которая заполнена данными города через вызов базы данных. Скелет компонента таблицы выглядит следующим образом:

import React, { useState } from 'react' 

function Table(props) {
  const [ cities, setCities ] = useState([])
  const [ pageNum, setPageNum ] = useState(0)

  useEffect(()=> { // this sets up the listener for the infinite scroll
    document.querySelector(".TableComponent").onscroll = () => {
      if (Math.ceil(table.scrollHeight - table.scrollTop) === table.clientHeight) showMoreRows()
    }
    //initial fetch
    fetchData(0)
  },[])

  async function showMoreRows() {
    console.log("Show more rows!")
    await fetchData(pageNum)
  }

  async function fetchData(page) {
    // some code, describing fetching
    // EDIT2 start
    console.log(pageNum)
    // EDIT2 end
    const jsonResponse = await {}// THE RESPONSE FROM THE FETCH
    if(page) {
      setCities([...cities, ...jsonResponse])
      console.log("page is true", page)
      setPageNum(pageNum + 1)
    } else {
      console.log("page is false", page) // this always runs and prints "page is false 0"
      setCities([...cities, ...jsonResponse])
      setPageNum(1)
    }
  }


  return <div className="TableComponent"> { pageNum } 
           <!-- The rest of the component --> 
         </div>
}

Таблица имеет функцию «бесконечной прокрутки», поэтому при прокрутке вниз страницы она печатает "Show more rows!" и запускает fetchData(pageNum), чтобы получить больше данных. В этот момент после первоначальной выборки переменная pageNum должна быть 1 или более, но по какой-то причине функция действует так, как будто она равна 0. Я выставил переменную pageNum на дисплее в JSX, и яЯ вижу, что это 1, но он все равно печатает "page is false 0", когда я запускаю его.

Когда я пытаюсь решить проблему, похоже, единственное, что может случиться, это то, что я пытаюсь прочитать переменную useState слишком рано после использования setPageNum (до перерисовки), но здесь это не так. насколько я вижу. Я даю ему много времени между попытками, и он всегда говорит, что pageNum - ноль.

Есть идеи о том, что я делаю неправильно, и как это имеет смысл в любом случае?

Спасибо за любую помощь!

РЕДАКТИРОВАТЬ: Просто попробовал код, который я переписал, и он, кажется, работает - однако полный код, который я имею, не работает. У кого-нибудь есть идеи по поводу проблем, связанных с этим, даже если приведенный выше код может работать?

EDIT2: я добавил console.log(pageNum) к функции fetchData и немного протестировал, и кажется, что все, что я положил в начальное значение в useState(VALUE), - это то, что печатается. Это имеет смысл NO для меня. Справка.

EDIT3: добавлено ожидание, оно уже есть в реальном коде

EDIT4: я пробовал это некоторое время, но понял, что с помощью реагирования я могу перемещать прокрутку-У меня слушатель до JSX-зоны, а потом все заработало - почему-то. Теперь это работает. Не могу пометить ответы как правильные, но проблема в некоторой степени решена.

Спасибо всем, кто пытался помочь, очень ценю это.

Ответы [ 3 ]

1 голос
/ 18 октября 2019

Ваши проблемы со стабильностью возникают из-за того, что React не знает о ваших зависимостях от состояния компонента.

Например, useEffect гарантирует, что значение showMoreRows из области начального рендеринга будет вызванона каждом свитке. Эта копия showMoreRows относится к начальному значению pageNum, но это значение «заморожено» в замыкании вместе с функцией и не изменится, когда состояние компонента изменится. Следовательно, прослушивание прокрутки не будет работать, так как ему нужно знать текущее состояние pageNum.

. Вы можете решить проблемы, используя callbacks , чтобы "перехватить" showMoreRows и fetchData и объявить их зависимость от состояния компонента. Затем вы должны объявить зависимость useEffect от этих обратных вызовов и использовать функцию очистки для обработки вызываемого эффекта более одного раза.

Это будет выглядеть примерно так (яне пытался запустить код):

import React, { useState } from 'react' 

function Table(props) {
  const [ cities, setCities ] = useState([])
  const [ pageNum, setPageNum ] = useState(0)

  useEffect(()=> { 
    // Run this only once by not declaring any dependencies
    fetchData(0)
  }, [])

  useEffect(()=> {
    // This will run whenever showMoreRows changes.

    const onScroll = () => {
      if (Math.ceil(table.scrollHeight - table.scrollTop) === table.clientHeight) showMoreRows()
    };

    document.querySelector(".TableComponent").onscroll = onScroll;

    // Clean-up
    return () => document.querySelector(".TableComponent").onscroll = null;
  }, [showMoreRows])

  const showMoreRows = React.useCallback(async function () {
    console.log("Show more rows!")
    await fetchData(pageNum)
  }, [fetchData, pageNum]);

  const fetchData = React.useCallback(async function (page) {
    // some code, describing fetching
    // EDIT2 start
    console.log(pageNum)
    // EDIT2 end
    const jsonResponse = await {}// THE RESPONSE FROM THE FETCH
    if(page) {
      setCities([...cities, ...jsonResponse])
      console.log("page is true", page)
      setPageNum(pageNum + 1)
    } else {
      console.log("page is false", page) // this always runs and prints "page is false 0"
      setCities([...cities, ...jsonResponse])
      setPageNum(1)
     }
  }, [setCities, cities, setPageNum, pageNum]);

  return <div className="TableComponent"> { pageNum } 
           <!-- The rest of the component --> 
         </div>
}
0 голосов
/ 18 октября 2019

Трудно сказать без контекста, но у меня есть одно предположение. Попробуйте использовать

setCities(value => [...value, ...jsonResponse])

вместо

setCities([...cities, ...jsonResponse])

Также убедитесь, что вы используете await для разрешения посылок по таким запросам, как:

const jsonResponse = await ...

Вы можете войти в консоль напроверьте, не ожидают ли они, и получите ли вы правильное свойство, если это вложенный объект.

0 голосов
/ 18 октября 2019

Это может не полностью решить проблему (трудно сказать без контекста), но useEffect запускает каждый рендер , поэтому такие вещи, как этот 'initial' fetchData(0), будут запускать каждое обновление, котороевероятно, даст вам результат из page = 0 каждый раз в этом условии в fetchData.

...