Реагировать клонировать объект из родительских реквизитов продолжает обновляться при повторном рендеринге

#javascript #arrays #reactjs #react-props

Вопрос:

Не знаю, как это исправить. Мы получаем массив объектов из нашего API и используем его для создания данных в представлении. В то же время в дочернем компоненте (фильтры) Я хочу сопоставить одно из полей в данных, чтобы создать свои категории фильтров. Фильтр вызовет новые данные из API с параметром, и эти данные будут возвращены и обновят реквизиты (я думаю, что это проблема, так как это перенаправляет дочерний компонент, используя отфильтрованные данные, чтобы снова задать категории).

Я думал, что помещение массива в useEffect остановит его обновление.

Вот текущий tsx в моем компоненте фильтра:

 const ExclusiveOffersFilters = (props: ExclusiveOffersFiltersProps): JSX.Element => {
  const newFilters = JSON.parse(JSON.stringify(props.options)); // copy the object from props
  let filterOffers;

  useEffect(() => {
    filterOffers = newFilters.map((item: any) => {
      return { value: item.offerType, display: item.offerType };
    });
    console.log('filterOffers: ', filterOffers); // succesfully logs the filtered items. 
  }, []);

  return (
    <tr>
      <th>
        <Filter
          options={filterOffers} // here filterOffers is undefined
          alignRight={props.alignRight}
          handleClick={props.handleFilterChange}
          multiSelect={props.multiSelect}
          selectedItems={props.selectedItems}
        />
      </th>
    </tr>
  );
};
 

Я также попробовал const newFilters = [...props.options]; и newFilters = props.options.slice(0) , но это та же проблема (казалось, разумнее было использовать JSON-анализ/stringify).

В моем родительском компоненте я называю ребенка так:

<ExclusiveOffersFilters handleFilterChange={props.handleFilterChange} options={props.options} />

Если я удалю useEffect функцию и просто обновлю фильтры, она будет работать, однако после выбора одного фильтра приложение обновит вызов api, получит обратно только отфильтрованные данные, и поэтому в фильтрах задана только одна категория. Что мне нужно предотвратить.

Мне нужен способ получить исходные категории и заморозить их обновление.

Редактировать

Вот наши примерные данные:

 const data = [
  {
    offerId: 1,
    partnerName: "Another offer 4",
    imageUrl:
      "https://www.auctsjpg-w263-h98-99382466814e33da0cfa3d5d2afd921c.jpg",
    description:
      "Save up to 10% on something. <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p> <p>Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> <p>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>",
    header: "Save up to 10%",
    enabled: true,
    deleted: false,
    offerType: "Motoring"
  },
  {
    offerId: 0,
    partnerName: "Amazing offer 4",
    imageUrl: "https://www.cGen",
    description:
      "Save up to 20% off* your business insurance thanks to our partnership with<BR/> Churchill Expert. Simply visit the Churchill Expert web page and choose the<BR/> insurance product to suit your business.<BR/>     Churchill Expert will automatically apply the discount to your premium for the<BR/>duration of your policy.     <BR/><BR/>*Minimum premiums apply. Discount applies to NIG policies arranged by Churchill Expert <BR/>and underwritten by U K Insurance Limited.<BR/> Professional Indemnity excluded.",
    header: "Save up to 20% off",
    enabled: true,
    deleted: false,
    offerType: "Insurance"
  },
  {
    offerId: 190,
    partnerName: "Amazing offer 4",
    imageUrl: "https://www.ch.Code=ExpertGen",
    description:
      "Save up to 20% off* your business insurance thanks to our partnership with<BR/> Churchill Expert. Simply visit the Churchill Expert web page and choose the<BR/> insurance product to suit your business.<BR/>     Churchill Expert will automatically apply the discount to your premium for the<BR/>duration of your policy.     <BR/><BR/>*Minimum premiums apply. Discount applies to NIG policies arranged by Churchill Expert <BR/>and underwritten by U K Insurance Limited.<BR/> Professional Indemnity excluded.",
    header: "Save up to 20% off",
    enabled: true,
    deleted: true,
    offerType: "Insurance"
  }
];

export default data;
 

Если мы отфильтруем по типу предложения = Автомобильное движение, то мы снова вызовем наш api с автомобильным движением в качестве фильтра, это вернет только 1 запись из приведенных выше данных, но теперь наш фильтр обновляется и показывает только 1 категорию фильтра автомобильного движения.

Смотрите прилагаемые примеры из приложения.

введите описание изображения здесь
введите описание изображения здесь

Я сбросил большую часть файлов сюда: https://codesandbox.io/s/charming-sinoussi-l2iwh?file=/data.js:0-2061 с образцами данных. Я не знаю, насколько это полезно, трудно привести рабочий пример, когда существует так много зависимостей.

Родительский index.tsx обрабатывает все вызовы api ajax и передает данные в реквизит. Файл ExclusiveOffersFilters.tsx получает это и обновляется, и это та часть, которую я хочу остановить. Как отмечает @Adam в комментариях, мы пытаемся это исправить, но я думаю, что на самом деле с родителем все в порядке, проблема в ребенке.

Мы действительно хотим применить эту логику в другом месте приложения, поэтому производительность будет хорошей. Мне интересно, нужно ли мне просто не использовать props.options и использовать для этого другую опору?

Комментарии:

1. Похоже, вы пытаетесь исправить проблему в своем дочернем компоненте, применив взлом в родительском компоненте (две ошибки не делают правильного). Опубликуйте код для дочернего компонента, и у меня такое чувство, что сразу станет очевидно, в чем проблема.

2. @Adam Я обновил свой вопрос, добавив ссылку на большинство файлов, примеры данных и скриншоты проблемы.

Ответ №1:

  1. useEffect выполняет асинхронно, поэтому filterOffers может быть неопределенным в момент рендеринга.
  2. Если вы хотите создать значение на основе какого-либо другого значения и хотите оптимизировать его, используйте useMemo hook.
  3. Если вы хотите сохранить значение между повторными визуализациями компонента, используйте useRef крюк.
     const newFilters = useRef(JSON.parse(JSON.stringify(props.options))); // copy the object from props
    const filterOffers = useMemo(() => {
         filterOffers = newFilters.current.map((item: any) => {
             return { value: item.offerType, display: item.offerType };
         });
         console.log('filterOffers: ', filterOffers); // succesfully logs the filtered items. 
    }, [newFilters]);
     

Комментарии:

1. Почему вы используете useRef ? Вы добавляете newFilters в качестве зависимости useMemo ? newFilters является рефератом и никогда не изменится.

2. Возможно, я неправильно понял вопрос, но идея заключалась в том, чтобы сохранить их между повторными рендерами.

3. Насколько я понял, OP хочет сохранить исходные фильтры между повторными визуализациями, поэтому я использую useRef

4. Эй, спасибо за это, я никак не могу заставить его работать. Но вы сделали filterOffers константой, а затем снова вызываете ее внутри функции useMemo. Я обновил свой вопрос ссылкой на ресурсы, примеры данных и скриншоты. Я думаю, что функция должна быть const filterOffers = useMemo(() => { return filterOffers = newFilters.current.map((item: any) => { return { value: item.offerType, display: item.offerType }; }); console.log('filterOffers: ', filterOffers); // succesfully logs the filtered items. }, [newFilters]);

5. @lharby извините, но в силу того , что этот ответ помещен newFilters в массив зависимостей useMemo , он ясно демонстрирует непоследовательность этого ответа. Это тебе не поможет.

Ответ №2:

Это все, что вам нужно. Если у вас возникнут проблемы с производительностью, используйте useMemo . Если у вас не возникнет проблем с производительностью, ничего не предпринимайте

 const ExclusiveOffersFilters = (props: ExclusiveOffersFiltersProps): JSX.Element => {

  const filters = props.options.map((item) => ({
    value: item.offerType, 
    display: item.offerType
  }));

  return (
    <tr>
      <th>
        <Filter
          options={filters}
          alignRight={props.alignRight}
          handleClick={props.handleFilterChange}
          multiSelect={props.multiSelect}
          selectedItems={props.selectedItems}
        />
      </th>
    </tr>
  );
};
 

Пример с useMemo

  const filters = useMemo(() => props.options.map(....),[props.options]);
 

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

Комментарии:

1. Спасибо, я попробую, но я подозреваю, что реквизит.параметры будут обновлены при повторном отправке.

2. @lharby Так и будет, но я перечитал ваш вопрос и две вещи-во — первых, вся информация о JSON.parse JSON.stringify-это чепуха, а во-вторых, вы пытаетесь исправить ошибку в своем нижнем компоненте, добавив взлом в этот компонент. Опубликуйте код для нижнего компонента, который, как вы говорите, продолжает обновляться, и исправьте ошибку там, где она есть на самом деле, вместо того, чтобы взламывать этот компонент.

3. Хорошо, я заберу это завтра и обновлю, большое вам спасибо за ваш вклад

4. @lharby — ваш кодовый код сейчас не работает, но я почти могу гарантировать, что проблема связана с вашим использованием splice и push Filter.tsx использованием . Когда дело доходит до реакции, не изменяйте ничего, состояние, реквизит, ничего.