Como usar o método do ciclo de vida getDerivedStateFromProps, em oposição ao componentWillReceiveProps

142

Parece que componentWillReceivePropsserá completamente eliminado nas próximas versões, em favor de um novo método de ciclo de vida getDerivedStateFromProps: static getDerivedStateFromProps () .

Após a inspeção, parece que agora você não consegue fazer uma comparação direta entre this.propse nextProps, como pode componentWillReceiveProps. Existe alguma maneira de contornar isso?

Além disso, agora ele retorna um objeto. Estou correto ao assumir que o valor de retorno é essencialmente this.setState?

Abaixo está um exemplo que encontrei on-line: Estado derivado de adereços / estado .

Antes

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

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

Depois de

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;
  }
}
Andrew
fonte

Respostas:

96

Sobre a remoção de componentWillReceiveProps: você deve poder lidar com seus usos com uma combinação de getDerivedStateFromPropse componentDidUpdate, consulte a postagem do blog React, por exemplo, migrações. E sim, o objeto retornado por getDerivedStateFromPropsatualiza o estado de maneira semelhante a um objeto passado para setState.

Caso você realmente precise do valor antigo de um suporte, sempre poderá armazená-lo em cache em seu estado com algo como isto:

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
  };
}

Qualquer coisa que não afete o estado pode ser colocada componentDidUpdate, e há até getSnapshotBeforeUpdatecoisas de nível muito baixo.

ATUALIZAÇÃO: Para ter uma idéia dos novos (e antigos) métodos do ciclo de vida, o pacote react-lifecycle-visualizer pode ser útil.

Oblosys
fonte
1
Ugh, eu errei a pergunta. Na verdade, eu queria dizercomponentWillReceiveProps
Andrew
2
Eu tinha pensado em usar meu estado para armazenar adereços anteriores, mas eu realmente queria evitar o código e a lógica extras necessários para implementá-lo. Vou examinar algumas das outras coisas que você menciona. Muito obrigado!
Andrew
4
Ter que armazenar um suporte anterior em um estado é apenas uma solução alternativa para essa alteração da API do React difícil de entender. Para os olhos de muitos desenvolvedores, isso parece um antipadrão e uma mudança de regressão. Não estou criticando você Oblosys, mas a equipe React.
AxeEffect
2
@AxeEffect Isso ocorre porque getDerivedStateFromPropsnunca foi realmente planejado para memorização . Por favor, veja minha resposta abaixo, onde descrevi a abordagem recomendada .
Dan Abramov
isso é um erro de digitação? Você perdeu ...? Ou seja, devemos retornar o objeto de estado inteiro ou apenas a parte com a qual nos preocupamos.
theprogrammer
51

À medida que recentemente postou no blog Reagir , na grande maioria dos casos você não precisa getDerivedStateFromPropsde todo .

Se você deseja calcular apenas alguns dados derivados, faça o seguinte:

  1. Faça certo por dentro render
  2. Ou, se o cálculo for caro, use um auxiliar de memorização memoize-one.

Aqui está o exemplo "depois" mais simples:

import memoize from "memoize-one";

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

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

Confira esta seção da postagem do blog para saber mais.

Dan Abramov
fonte
45
Se não for necessário na grande maioria dos casos, estou surpreso que tenha sido uma mudança tão necessária, a que quebrará milhares de projetos em funcionamento. Parece que a equipe do React iniciou a engenharia.
Ska
39
Mude de componentWillReceiveProps para getDerivedStateFromProps. Não está quebrando, mas forçando a refatorar todo o código existente, o que consome muito tempo. E parece ter muito pouco benefício, pois você diz que não deveria usá-lo na grande maioria dos casos. Por que enfrentar o incômodo de alterar a API para algo que nem deveria ser usado em primeiro lugar.
Ska
4
Eu adoraria uma resposta a este comentário de Dan Abramov.
Louis345
6
@DanAbramov alguma resposta sobre por que essa mudança aconteceu?
Petros Kyriakou
3
Na verdade, em nossos projetos, isso é muito usado. Para mostrar coisas como Snackbars nas telas para quando novos dados são lançados, 1 exemplo. componentWillReceivePropsera simples e funcionou. Por que removê-lo para este lixo estática ...
Oliver Dixon
6

Como mencionado por Dan Abramov

Faça direito dentro da renderização

Na verdade, usamos essa abordagem com o memoise one para qualquer tipo de suporte de proxy para cálculos de estados.

Nosso código fica assim

// ./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>
    );
  }
}

Os benefícios são que você não precisa codificar toneladas de clichês de comparação dentro getDerivedStateFromPropsou componentWillReceivePropspode pular a inicialização de copiar e colar dentro de um construtor.

NOTA:

Essa abordagem é usada apenas para fazer o proxy dos props para indicar, caso você tenha alguma lógica de estado interno, ela ainda precisará ser manipulada nos ciclos de vida dos componentes.

mpospelov
fonte