UseMemo не перерисовывает динамические элементы управления

У меня есть список элементов управления, которые отправляются бэкэндом. Иногда необходимо обновить параметры раскрывающегося списка.

Я думал, что это сработает. Однако memoizedControls не перерисовывается.

Я считаю, что это должно работать так:

  • нажмите кнопку, и функция handleFieldsChange() сработает.
  • затем setTechSpec('') устанавливает techSpec на ''
  • затем срабатывает пользовательский хук usePerson, потому что он имеет techSpec в своем массиве зависимостей
  • тогда memoizedControls запускается, потому что он имеет personList в своем массиве зависимостей
  • и updateControlOptions() обновляет параметры controls

Однако пользовательский интерфейс не перерисовывался, и новые параметры personList не перерисовывались.

const [techSpec, setTechSpec] = useState('1')
const [personList, isLoadingPersonList] = usePerson(techSpec)

const handleFieldsChange = (changedValue: EditablePersonValues) => {
    setTechSpec('')
    fetchPerson()}

const updateControlOptions = (
    controls: FormControl[],
    controlName: string,
    newOptions: SelectOption[],
) =>
    controls.map((control) =>
        control.name === controlName
            ? { ...control, options: newOptions }
            : { ...control },)

const memoizedControls = useMemo(() => {
    console.info('memoizedControls')
    if (personList.length > 0)
        return updateControlOptions(
            controls,
            'personId',
            personList,
        )
    return controls
}, [controls, personList])

const fetchPerson = () => {
    const localTechSpecification = form.getFieldValue('techSpecification')
    setTechSpec(localTechSpecification)
    form.setFieldsValue({ personId: undefined })
}

и:

return (
{memoizedControls.map(
        ({ name, type, displayName, required, options, measure }) => {                          
            return (
                <MyDynamicField
                    key = {name}
                    name = {name}
                    controlType = {type}
                    displayName = {`${displayName}${measure ? `, ${measure}` : ''}`}
                    required = {required}
                    value = {value}
                    itemOptions = {options}
                />
            )
        },
    )}
)

Мой вопрос заключается в том, что хук «usePerson» выполняется повторно при изменении значения состояния «techSpec». personList обновляется. Но memoizedControls не показывает новые значения personList. Может быть, вы знаете, почему memoizedControls не перерисовывается?

Пожалуйста, кто-нибудь знает, что я делаю неправильно?

Было бы полезно, если бы вы создали codeandbox (или, что предпочтительнее, встроенный фрагмент кода), который воспроизводит эту проблему, чтобы мы могли помочь в отладке.

Nick Parsons 28.12.2022 12:07

@NickParsons, возможно, можно внести некоторые предложения без песочницы, потому что в песочницу нужно импортировать так много зависимостей. Спасибо за желание помочь

Learner 28.12.2022 12:56

Вам не нужно заново создавать весь проект в codesanbox, только код, который потребуется для воспроизведения вашей проблемы. Немного сложно увидеть, что работает/не работает, не имея чего-то, с чем мы можем работать и играть. Только из этого кода я ничего не могу заметить, может быть, кто-то еще может.

Nick Parsons 28.12.2022 13:07

@NickParsons большое спасибо. Ошибка была не в приведенном выше коде, ошибка была в компоненте MyDynamicField

Learner 29.12.2022 08:27
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
4
68
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

import React, { useState, useMemo } from 'react';
const MyComponent = ({ personList }) => {
  const [techSpec, setTechSpec] = useState('1');
  const [isLoadingPersonList, setIsLoadingPersonList] = useState(false);

  const handleFieldsChange = (changedValue) => {
    setTechSpec('');
    fetchPerson();
  };

  const updateControlOptions = (controls, controlName, newOptions) =>
    controls.map((control) =>
      control.name === controlName
        ? { ...control, options: newOptions }
        : { ...control }
    );

  const memoizedControls = useMemo(() => {
    console.info('memoizedControls');
    if (personList.length > 0) {
      return updateControlOptions(controls, 'personId', personList);
    }
    return controls;
  }, [controls, personList]); // personList is included in the dependency array

  const fetchPerson = () => {
    const localTechSpecification = form.getFieldValue('techSpecification');
    setTechSpec(localTechSpecification);
    form.setFieldsValue({ personId: undefined });
  };

  return (
    <div>
      {memoizedControls.map(({ name, type, displayName, required, options, measure }) => (
        <DynamicField
          key = {name}
          name = {name}
          controlType = {type}
          displayName = {`${displayName}${measure ? `, ${measure}` : ''}`}
          required = {required}
          value = {value}
          itemOptions = {options}
        />
      ))}
    </div>
  );
};

да, и это желательное поведение. personList обновляется, но memoizedControls не показывает новые значения personList. Может быть, вы знаете, почему memoizedControls не перерисовывается?

Learner 28.12.2022 11:56

компонент, который отвечает за рендеринг значения memoizedControls, использует компонент более высокого порядка PureComponent или React.memo, что предотвращает повторный рендеринг компонента, когда его реквизиты не меняются. В этом случае вы можете либо удалить компонент более высокого порядка PureComponent или React.memo, либо сделать так, чтобы компонент выполнял повторный рендеринг, изменив один из его реквизитов при изменении значения personList.

Farouk Allani 28.12.2022 12:04

Я извиняюсь за незнание, но вы покажите, пожалуйста, это через код?

Learner 28.12.2022 12:08

Единственное отличие, которое я нашел, это const [isLoadingPersonList, setIsLoadingPersonList] = useState(false);, и результат тот же, элементы управления не перерисовываются.

Learner 28.12.2022 13:28
Ответ принят как подходящий

Приведенный выше код был в порядке, проблема была в компоненте <MyDynamicField/>.

Поэтому itemOptions следует добавить в массив зависимостей [height, width, itemOptions]) из useEffect. Таким образом, код будет выглядеть так:

export const MyDynamicField = ({
    name,
    controlType,
    displayName,
    required,
    value,
    itemOptions,
    moulds,
    initData,
    height,
    width,
}: Props) => {
    const [options, setOptions] = useState<SelectOption[]>([])

    useEffect(() => {
        const prepareOptions = filterOptions(
            name,
            moulds,
            initData,
            itemOptions,
            height,
            width,
        )
        setOptions(prepareOptions)
    }, [height, width, itemOptions])
    return (
        <ControlFactory
            key = {name}
            name = {name}
            controlType = {controlType}
            displayName = {displayName}
            required = {required}
            options = {options}
            value = {value}
        />
    )
}

а затем useEffect и useState можно удалить, а useMemo можно использовать:

export const MyDynamicField = ({
    name,
    controlType,
    displayName,
    required,
    value,
    itemOptions,
    moulds,
    initData,
    height,
    width,
}: Props) => {
    const options = useMemo(
        () => filterOptions(name, moulds, initData, itemOptions, height, width),
        [name, moulds, initData, itemOptions, height, width],
    )

    return (
        <ControlFactory
            key = {name}
            name = {name}
            controlType = {controlType}
            displayName = {displayName}
            required = {required}
            options = {options}
            value = {value}
        />
    )
}

Другие вопросы по теме