Где я должен поставить некоторые операции в функциональном компоненте - PullRequest
0 голосов
/ 03 апреля 2020

Я получил некоторые данные с оконечных точек grpahql. Я должен сделать некоторые вычисления перед рендерингом. Но этот код вызывает зависание браузера после стольких раз рендеринга этого компонента. Я думаю, что я могу включить этот код для использования эффекта. Но я не могу понять, почему браузер зависает и как изменить код, чтобы он работал и работал хорошо. Как видите, в этом коде есть какая-то ошибка. Можете ли вы объяснить, как это сделать?

const StatusBar = () => {
  const { data: curProgram_data } = useQuery(GET_CUR_PROGRAM);
  const { data: programs_data, loading: programs_loading } = useQuery(GET_PROGRAMS);
  const { data: tests_data, loading: tests_loading } = useQuery(GET_TESTS);
  const { data: articles_data, loading: articles_loading } = useQuery(GET_ARTICLES);

  if (programs_loading || tests_loading || articles_loading)
    return <p>Loading ... </p>;

  const { curProgram } = curProgram_data;
  const { programs } = programs_data;
  const totalTests = tests_data.tests;
  const totalArticles = articles_data.articles;

  var startYear = moment(totalTests[0].date, 'YYYY/MM/DD').format('YYYY');
  var endYear = moment(totalTests[totalTests.length - 1].date, 'YYYY/MM/DD').format('YYYY');
  var years = [];
  for(var i = startYear; i <= endYear; i ++)
    years.push(i);

  for(var i = 0; i < programs.length; i ++) // Sort Programs by Date
    for(var j = i + 1; j < programs.length; j ++) {
      if(programs[i].tests[0].date > programs[j].tests[0].date) {
        var temp;
        temp = programs[i];
        programs[i] = programs[j];
        programs[j] = temp;
    }
  }
  console.log("I think we have to turn all variables to state with useEffect"); 

  var programsGroupedByYear = [];
  var eachProgramLength = [];

  var startYear = moment(programs[0].tests[0].date, 'YYYY/MM/DD').format('YYYY');
  var endYear = moment(programs[programs.length - 1].tests[0].date, 'YYYY/MM/DD').format('YYYY');

  for(i = 0; i < endYear - startYear + 1; i ++) {
    programsGroupedByYear[i] = [];
    eachProgramLength[i] = [];
  }

  var isNext = startYear;
  var yearIt = 0;
  for(i = 0; i < programs.length; i ++) { // Group Programs by Year
    var year = moment(programs[i].tests[0].date, 'YYYY/MM/DD').format('YYYY');

    if(year == isNext)
      programsGroupedByYear[yearIt].push(programs[i]);
    else {
      isNext = year;
      yearIt ++;
      programsGroupedByYear[yearIt].push(programs[i]);
    }
  }
  ...  ....

  return (
    <div>
      <YearBar 
        groupLengths = {groupLengths} 
        curProgramPos = {curProgramPos}
        years = {years}
        totalArticles = {totalArticles}
      />

      ....

    </div>
  )
}

1 Ответ

0 голосов
/ 03 апреля 2020

Реагирует на тот факт, что все динамические данные c должны быть в своем состоянии. Это включает даже данные DOM, такие как входные значения формы. То, что вы здесь делаете, совершенно неверно.

Слишком много ошибок вы делаете здесь

Ошибка 1:

Не используется React State. Не могу выделить это достаточно. Все ваши динамические данные c должны go перейти в состояние реакции - year, totalTests - все должно быть объявлено как состояние реакции. Таким образом React может отслеживать изменения данных и оптимизировать производительность.

Ошибка 2:

Не использовать useEffect() для выполнения побочных эффектов. Побочный эффект не должен выполняться при root функции. Тебе не следует. Реакт никогда не узнает, что, черт возьми, происходит внутри вашей функции. Используйте правильный useEffect() с соответствующим массивом зависимостей

Ошибка 3:

Это ошибка JavaScript. Не используйте var для объявления переменных. Имеет проблемы с областями видимости. Используйте let для переменных и const для констант.

Ошибка 4:

Не используйте эффективные и встроенные JavaScript методы вместо написания собственной сортировки и других алгоритмов.

Консольный журнал в этом компоненте вызывает инфинитив. Почему?

Поскольку console.log находится в root функции. Каждый раз, когда происходит обновление, и когда React необходимо повторно выполнить рендеринг компонента, функция будет вызываться снова с новыми значениями состояния. Так как ваш код заставил функцию работать бесконечно l oop, запись в консоль осуществлялась бесконечно. Вот почему гораздо важнее использовать useState и useEffects, чтобы React знал, что выполнять и что обновлять и не допускать бесконечного l oop.

Надеюсь, что он ответит ваши вопросы, следующий переход к решению

Решение

Я должен дать понять, что не могу и не буду переписывать весь ваш код, и убедиться, что все работает, это ваша работа, но я, однако, я Я более чем рад помочь вам и объяснить их.

Прежде чем перейти к React, позвольте мне прояснить ситуацию в JavaScript space

Избегать использования var

var неправильно определены в JavaScript, что приводит к большим путаницам и головным болям при отладке. Поэтому используйте новые ключевые слова для объявления переменных

  • let для переменных
  • const для константы

Они ограничены и ведут себя так же, как вы ожидайте, что они будут вести себя.

Если что-то существует, не переписывайте это - используйте встроенные методы

Вместо написания собственного алгоритма сортировки, используйте JavaScript встроенный sort метод. Объятие веселье национальное программирование

Ваш алгоритм сортировки можно заменить на

const sortedPrograms = [ ...programs ].sort((a, b) => 
  a.tests[0].date > b].tests[0].date ? -1 : 1
)

Причина, по которой я использовал [ ...programs ].sort вместо programs.sort, заключается в том, что sort метод изменяет исходный массив, что нежелательно в React, поэтому я мелко клонировал массив.

Для следующих двух циклов for, я полагаю, вы пытаетесь сгруппировать программы по годам. (Поправьте меня, если я ошибаюсь).

Эти десятки строк кода можно легко заменить моим любимым JavaScript reduce методом.

Ваш алгоритм группировки можно заменить на

const programsGroupedByYear = programs.reduce(
  // acc - Accumulated, cur - current item
  (acc, cur) => {
    const year = moment(cur.tests[0].date, 'YYYY/MM/DD').format('YYYY');
    return {
      ...acc,
      // Check if the year index exist in the accumulated object
      // if yes, append it to the array
      // if no, create a new array
      // and store in the year index
      [year]: acc[year] ? [ ...acc[year], cur ] : [cur]
    }
  }
, {})

Вот и все, ваши 20+ строк из двух для циклов могут быть заменены на 6+ строк Array.reduce метода.

Сохраните все данные в состоянии реакции

Аполлон useQuery представляет собой пользовательский хук, что означает, что вывод data является состоянием реакции. Поэтому необходимо объявить любое новое состояние здесь, кроме переменной для хранения отсортированных и сгруппированных данных.

Переместить все побочные эффекты внутрь useEffects

Не должно быть никаких побочных эффектов на root функции. Все они должны go внутри useEffects

useEffect(() => {
  // All your side effects goes in here
}, [deps])
// React will track the items in dep array, 
// if there's any change in them, React will call this useEffect

Если побочные эффекты не разрешены в root функции, что тогда должно быть разрешено в root? Это приводит нас к

Всегда используйте хуки в root вашей функции

Не вызывайте хуки внутри циклов, условий или вложенных функций.

Заключительный совет

Используйте JavaScript как способ функционального программирования, особенно если вы используете React вместо того, чтобы думать о циклах и мутации данных

Я очень предлагаю вам использовать Rule of Hooks плагин для линтера.

Итак, завершение,

GIST вашего кода

const StatusBar = () => {
  const { data: curProgram_data } = useQuery(GET_CUR_PROGRAM);
  const { data: programs_data, loading: programs_loading } =
    useQuery(GET_PROGRAMS);
  const { data: tests_data, loading: tests_loading } = useQuery(GET_TESTS);
  const { data: articles_data, loading: articles_loading } =
    useQuery(GET_ARTICLES);

  const [sortedProgram, setSortedProgram] = useState([]);
  const [programsGroupedByYear, setProgramsGroupByYear] =
    useState([]);

  useEffect(() => {
    if (!programs_loading) {
      const _sortedPrograms = [ ...programs_data.programs ].sort((a, b) => 
        a.tests[0].date > b].tests[0].date ? -1 : 1
       )
      setSortedProgram(_sortedPrograms)

      const _programsGroupedByYear = programs.reduce(
        (acc, cur) => {
          const year = moment(cur.tests[0].date, 'YYYY/MM/DD').format('YYYY');
          return {
            ...acc,
            [year]: acc[year] ? [ ...acc[year], cur ] : [cur]
          }
        }
        , {})
      }
      setProgramsGroupedByYear(__programsGroupedByYear)
  }, [programs_data, programs_loading])


  // Then place your if condition

  if (programs_loading || tests_loading || articles_loading)
    return <p>Loading ... </p>;

  return (
    <YourJSXCode />
  )
}

...