Jak używać metody cyklu życia getDerivedStateFromProps w przeciwieństwie do componentWillReceiveProps

142

Wygląda na componentWillReceivePropsto, że w nadchodzących wersjach zostanie całkowicie wycofane na korzyść nowej metody cyklu życia getDerivedStateFromProps: statycznej getDerivedStateFromProps () .

Po sprawdzeniu wygląda na to, że nie możesz teraz dokonać bezpośredniego porównania między this.propsi nextProps, tak jak możesz w componentWillReceiveProps. Czy można to obejść?

Ponadto zwraca teraz obiekt. Czy mam rację, zakładając, że wartość zwracana jest zasadniczo this.setState?

Poniżej znajduje się przykład, który znalazłem online: Stan wyprowadzony z właściwości / stanu .

Przed

class ExampleComponent extends React.Component {
  state = {
    derivedData: computeDerivedState(this.props)
  };

  componentWillReceiveProps(nextProps) {
    if (this.props.someValue !== nextProps.someValue) {
      this.setState({
        derivedData: computeDerivedState(nextProps)
      });
    }
  }
}

Po

class ExampleComponent extends React.Component {
  // Initialize state in constructor,
  // Or with a property initializer.
  state = {};

  static getDerivedStateFromProps(nextProps, prevState) {
    if (prevState.someMirroredValue !== nextProps.someValue) {
      return {
        derivedData: computeDerivedState(nextProps),
        someMirroredValue: nextProps.someValue
      };
    }

    // Return null to indicate no change to state.
    return null;
  }
}
Andrzej
źródło

Odpowiedzi:

96

O usunięciu componentWillReceiveProps: powinieneś być w stanie obsłużyć jego użycie za pomocą kombinacji getDerivedStateFromPropsi componentDidUpdate, zobacz post na blogu React, na przykład migracje. I tak, obiekt zwrócony przez getDerivedStateFromPropsaktualizuje stan podobnie jak obiekt przekazany do setState.

Jeśli naprawdę potrzebujesz starej wartości rekwizytu, zawsze możesz buforować ją w swoim stanie za pomocą czegoś takiego:

state = {
  cachedSomeProp: null
  // ... rest of initial state
};

static getDerivedStateFromProps(nextProps, prevState) {
  // do things with nextProps.someProp and prevState.cachedSomeProp
  return {
    cachedSomeProp: nextProps.someProp,
    // ... other derived state properties
  };
}

Wszystko, co nie wpływa na stan, może zostać wprowadzone componentDidUpdate, a nawet jest to getSnapshotBeforeUpdatedla rzeczy bardzo niskiego poziomu.

AKTUALIZACJA: Aby zapoznać się z nowymi (i starymi) metodami cyklu życia, pomocny może być pakiet „ React-Lifecycle-visualizer” .

Oblosys
źródło
1
Ugh, spieprzyłem pytanie. Właściwie to miałem na myślicomponentWillReceiveProps
Andrew
2
Myślałem o użyciu mojego stanu do przechowywania poprzednich właściwości, ale naprawdę chciałem uniknąć dodatkowego kodu i logiki potrzebnej do jego implementacji. Przyjrzę się innym sprawom, które poruszysz. Wielkie dzięki!
Andrew
4
Konieczność przechowywania poprzedniej właściwości w stanie to tylko standardowe obejście tej trudnej do zrozumienia zmiany interfejsu API React. W oczach wielu deweloperów wygląda to jak antywzór i zmiana regresji. Nie krytykuje cię Oblosys, ale zespół React.
AxeEffect
2
@AxeEffect Dzieje się tak, ponieważ getDerivedStateFromPropsnigdy nie był przeznaczony do zapamiętywania . Zobacz moją odpowiedź poniżej, gdzie zamiast tego opisałem zalecane podejście .
Dan Abramov
czy to jest literówka? Tęskniłeś ...? Czyli powinniśmy zwrócić cały obiekt stanu lub tylko tę część, na której nam zależy.
theprogrammer
51

Jak niedawno opublikowaliśmy na blogu React , w większości przypadków nie potrzebujesz go getDerivedStateFromPropswcale .

Jeśli chcesz tylko obliczyć niektóre dane pochodne:

  1. Zrób to w środku render
  2. Lub, jeśli ponowne obliczenie jest drogie, użyj pomocnika do zapamiętywania, takiego jak memoize-one.

Oto najprostszy przykład „po”:

import memoize from "memoize-one";

class ExampleComponent extends React.Component {
  getDerivedData = memoize(computeDerivedState);

  render() {
    const derivedData = this.getDerivedData(this.props.someValue);
    // ...
  }
}

Sprawdź tę sekcję w poście na blogu, aby dowiedzieć się więcej.

Dan Abramov
źródło
45
Jeśli w zdecydowanej większości przypadków nie jest to potrzebne , to dziwię się, że była to tak bardzo potrzebna zmiana, która zepsuje tysiące działających projektów. Wygląda na to, że zespół React zaczął od inżynierii.
Ska
39
Zmień z componentWillReceiveProps na getDerivedStateFromProps. Nie jest to łamanie, ale wymuszanie refaktoryzacji całego istniejącego kodu, co jest bardzo czasochłonne. Wydaje się, że jest to bardzo mało korzystne, ponieważ mówisz, że w większości przypadków nie powinieneś go używać. Po co męczyć się ze zmianą API na coś, czego w ogóle nie powinno się używać.
Ska
4
Bardzo chciałbym odpowiedzieć na ten komentarz Dana Abramowa.
Louis345
6
@DanAbramov jakaś odpowiedź na pytanie, dlaczego doszło do tej zmiany?
Petros Kyriakou
3
Właściwie w naszych projektach jest to często używane. 1 przykład za pokazywanie na ekranach takich elementów, jak paski przekąsek, gdy pojawiają się nowe dane. componentWillReceivePropsbył prosty i działał. Po co go usuwać z powodu tych statycznych śmieci ...
Oliver Dixon
6

Jak wspomniał Dan Abramov

Zrób to bezpośrednio wewnątrz renderowania

W rzeczywistości używamy tego podejścia z memoise dla dowolnego rodzaju podpowiedzi zastępczych do obliczeń stanu.

Nasz kod wygląda w ten sposób

// ./decorators/memoized.js  
import memoizeOne from 'memoize-one';

export function memoized(target, key, descriptor) {
  descriptor.value = memoizeOne(descriptor.value);
  return descriptor;
}

// ./components/exampleComponent.js
import React from 'react';
import { memoized } from 'src/decorators';

class ExampleComponent extends React.Component {
  buildValuesFromProps() {
    const {
      watchedProp1,
      watchedProp2,
      watchedProp3,
      watchedProp4,
      watchedProp5,
    } = this.props
    return {
      value1: buildValue1(watchedProp1, watchedProp2),
      value2: buildValue2(watchedProp1, watchedProp3, watchedProp5),
      value3: buildValue3(watchedProp3, watchedProp4, watchedProp5),
    }
  }

  @memoized
  buildValue1(watchedProp1, watchedProp2) {
    return ...;
  }

  @memoized
  buildValue2(watchedProp1, watchedProp3, watchedProp5) {
    return ...;
  }

  @memoized
  buildValue3(watchedProp3, watchedProp4, watchedProp5) {
    return ...;
  }

  render() {
    const {
      value1,
      value2,
      value3
    } = this.buildValuesFromProps();

    return (
      <div>
        <Component1 value={value1}>
        <Component2 value={value2}>
        <Component3 value={value3}>
      </div>
    );
  }
}

Zaletą tego rozwiązania jest to, że nie musisz kodować wielu wzorców porównawczych wewnątrz getDerivedStateFromPropslub componentWillReceivePropsmożesz pominąć inicjalizację kopiuj-wklej wewnątrz konstruktora.

UWAGA:

To podejście jest używane tylko do pośredniczenia właściwości w celu określenia, jeśli masz jakąś logikę stanu wewnętrznego, nadal musi być obsługiwana w cyklach życia komponentów.

mpospelov
źródło