ComponentWillReceiveProps против getDerivedStateFromProps

Что именно componentWillReceiveProps и getDerivedStateFromProps - тонкий вопрос для меня. Потому что я столкнулся с проблемой при использовании getDerivedStateFromProps:

// Component 
state = {
  myState: []
}

// Using this method works fine:

componentWillReceiveProps(nextProps) {
  this.setState({
    myState: nextProps.myPropsState
  })
}

// But using this method will cause the checkboxes to be readonly:

static getDerivedStateFromProps(nextProps,prevProps) {
  const { myPropsState: myState } = nextProps
  return {
    myState
  }
}

// And here's checkbox
<input type = "checkbox" id = {`someid`} 
 onChange = {(e) => this.handleMethod(e, comp.myState)} 
 checked = {myState.indexOf(comp.myState) > -1} />

Версия React: 16.4.1

Параметрами, переданными в getDerivedStateFromProps (), являются текущие props и state, а не nextProps и prevProps. Он должен возвращать следующее состояние в зависимости от текущего состояния и свойств. Пожалуйста, внимательно прочтите хотя бы документацию.

trixn 03.08.2018 13:34

это не должно иметь значения здесь, однако, имя параметра может быть любым, но я только что написал для сравнения, получит реквизиты. Кроме того, я здесь, чтобы узнать, почему флажки доступны только для чтения, когда я использую производное состояние?

Bhojendra Rauniyar 03.08.2018 13:40

Как это помогает сравнивать их, называя свои параметры чем-то, что они не представляют? Также Безоговорочное обновление состояния из реквизита считается анти-паттерном.

trixn 03.08.2018 13:47
Поведение ключевого слова "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) для оценки ваших знаний,...
10
3
5 530
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

getDerivedStateFromProps не является прямой альтернативой componentWillReceiveProps исключительно из-за того, что он вызывается после каждого обновления, будь то изменение состояния, изменение свойств или повторный рендеринг родительского объекта.

Однако в любом случае простое возвращение состояния из getDerivedStateFromProps - неправильный путь, вам необходимо сравнить состояние и свойства перед возвратом значения. В противном случае с каждым обновлением состояние сбрасывается до props, и цикл продолжается.

Согласно документы

getDerivedStateFromProps is invoked right before calling the render method, both on the initial mount and on subsequent updates. It should return an object to update the state, or null to update nothing.

This method exists for rare use cases where the state depends on changes in props over time. For example, it might be handy for implementing a <Transition> component that compares its previous and next children to decide which of them to animate in and out.

Deriving state leads to verbose code and makes your components difficult to think about. Make sure you’re familiar with simpler alternatives:

If you need to perform a side effect (for example, data fetching or an animation) in response to a change in props, use componentDidUpdate lifecycle instead.

If you want to re-compute some data only when a prop changes, use a memoization helper instead.

If you want to “reset” some state when a prop changes, consider either making a component fully controlled or fully uncontrolled with a key instead.

P.S. Обратите внимание, что аргументы для getDerivedStateFromProps - это props и state, а не nextProps и prevProps.

Чтобы получить более подробную информацию,

Чтобы вносить изменения на основе изменения реквизита, нам нужно сохранить prevPropsState в состоянии, чтобы обнаруживать изменения. Типичная реализация будет выглядеть так:

static getDerivedStateFromProps(props, state) {
    // Note we need to store prevPropsState to detect changes.
    if (
      props.myPropsState !== state.prevPropsState
    ) {
      return {
        prevPropsState: state.myState,
        myState: props.myPropsState
      };
    }
    return null;
  }

Спасибо за Ваш ответ. Но все же я столкнулся с той же проблемой.

Bhojendra Rauniyar 03.08.2018 13:57

@BhojendraNepal Прочтите это официальное сообщение в блоге о том, как справляться с такими ситуациями. Использование getDerivedStateFromProps, вероятно, вообще не лучший выбор для вашей проблемы.

trixn 03.08.2018 13:59

@BhojendraNepal, если myState является массивом, тогда прямая проверка props.myPropsState !== state.prevPropsState не удастся, вам нужно либо глубоко сравнить массив, либо использовать неизменяемый список

Shubham Khatri 03.08.2018 14:03

@ShubhamKhatri Следует использовать новый key (или id, если повторно инициализировать компонент дорого), переданный от родителя, чтобы разрешить сброс состояния от родителя путем передачи новых свойств. Это все описывается здесь. Мне не кажется, что глубокое сравнение объектов перед каждым рендером - хорошее решение.

trixn 03.08.2018 14:07

@trixn Лучший способ справиться с такими случаями - это мемоизация и использование неизменяемых списков объектов. Я дополню ответ более подробной информацией, когда у меня будет больше времени ночью

Shubham Khatri 03.08.2018 14:10

Я не хочу использовать мемоизацию. Есть ли другое решение, кроме производного состояния?

Bhojendra Rauniyar 03.08.2018 14:11

@BhojendraNepal, почему вы не хотите использовать мемоизацию.

Shubham Khatri 03.08.2018 14:12

@Shubham Khatri Лучший способ повторно вычислить состояние - это запоминание. Однако в этом примере ничего не вычисляется. Итак, лучший и официально рекомендуемый способ - использовать key или id для сигнализации сброса. Или, конечно, вы можете просто сделать это полностью управляемым и позволить родителю обрабатывать обновления (наиболее эффективно).

trixn 03.08.2018 14:14

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

Bhojendra Rauniyar 03.08.2018 14:16

В этом примере ничего не вычисляется, потому что всякий раз, когда компонент будет получать реквизиты, я всегда этого хочу, однако попробовал ваше предложение, возвращающее значение null, но не помогло.

Bhojendra Rauniyar 03.08.2018 14:18

@BhojendraNepal Да, все в порядке, я не хотел сказать, что ты должен что-то вычислить. Но просто повторюсь: если вы хотите (частично) сбросить состояние дочернего элемента, передав новые реквизиты, вы должны либо использовать key, либо id, которые вы передаете, и сравнивать, изменилось ли оно, или полностью поднять состояние до родительского, чтобы позволить он обрабатывает изменения. Это очень подробно описано в это официальное сообщение в блоге, повторюсь еще раз. Не знаю, почему вы отказываетесь его читать.

trixn 03.08.2018 14:21

Из ответных документов:

Note that this method is fired on every render, regardless of the cause. This is in contrast to UNSAFE_componentWillReceiveProps, which only fires when the parent causes a re-render and not as a result of a local setState.

Вы эффективно переопределяете свое состояние с помощью текущих свойств каждый раз после вызова setState(). Поэтому, когда вы устанавливаете флажок, вызывается (e) => this.handleMethod(e, comp.myState), который предполагает вызовы setState() для обновления отмеченного состояния флажка. Но после этого будет вызван getDerivedStateFromProps() (перед рендерингом), который отменяет это изменение. Вот почему Безоговорочное обновление состояния из реквизита считается анти-паттерном.

Ответ принят как подходящий

Наконец, я решил свою проблему. Отладка была мучительной:

// Child Component

// instead of this
// this.props.onMyDisptach([...myPropsState])

// dispatching true value since myPropsState contains only numbers
this.props.onMyDispatch([...myPropsState, true])

Это потому, что у меня есть два условия: 1) при изменении флажка (компонент) 2) при нажатии кнопки сброса (дочерний компонент)

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

Теперь, здесь, в компоненте, я нашел некоторые подсказки о различиях между componentWillReceiveProps и getDerivedStateFromProps после отладки вывода консоли.

// Component
static getDerivedStateFromProps(props, state) {
    const { myPropsState: myState } = props
    // if reset button is pressed
    const true_myState = myState.some(id=>id===true)
    // need to remove true value in the store
    const filtered_myState = myState.filter(id=>id!==true)
    if (true_myState) {
      // we need to dispatch the changes to apply on its child component
      // before we return the correct state
      props.onMyDispatch([...filtered_myState])
      return {
        myState: filtered_myState
      }
    }
    // obviously, we need to return null if no condition matches
    return null
  }

Вот что я нашел результаты вывода консоли:

  • getDerivedStateFromProps регистрирует сразу же при изменении свойств

  • componentWillReceiveProps регистрирует только после того, как дочерний элемент распространяет изменения реквизита

  • getDerivedStateFromProps не отвечает на изменения реквизита (я имел в виду изменения отправки, как в примере кода)

  • componentWillReceiveProps реагирует на изменения реквизита

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

Процесс вставки истинного значения в состояние, которое мне нужно, потому что getDerivedStateFromProps обрабатывает все изменения, в отличие от componentWillReceiveProps, обрабатывает только дочерний компонент, отправляющий изменения в реквизиты.

Кстати, вы можете использовать настраиваемое свойство, чтобы проверить, изменилось ли оно, и обновить значение, если getDerivedStateFromProps, но по какой-то причине мне нужно настроить эту технику.

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

Элементы в списках обычно должны быть одного типа. Также вы просматриваете весь список, чтобы найти это значение. Обратите внимание, что вы делаете это на каждом рендере, хотя, вероятно, в 99% случаев внутри boolean нет. Почему бы просто не передать еще одну опору вместе со сбросом, который вы проверяете, изменился ли он? Например. счетчик с автоинкрементом, как это предлагается в ответном сообщении в блоге? Это будет намного эффективнее, проще и менее подвержено ошибкам. Вы писали, что «getDerivedStateFromProps не реагирует на изменения реквизита». Это неправда. Он реагирует на каждое изменение свойств или состояния.

trixn 06.08.2018 11:22

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

Bhojendra Rauniyar 06.08.2018 12:47

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