Cómo utilizar el método de ciclo de vida getDerivedStateFromProps en lugar de componentWillReceiveProps

142

Parece componentWillReceivePropsque se eliminará por completo en las próximas versiones, a favor de un nuevo método de ciclo de vida getDerivedStateFromProps: static getDerivedStateFromProps () .

Tras la inspección, parece que ahora no puede hacer una comparación directa entre this.propsy nextProps, como puede hacerlo componentWillReceiveProps. ¿Hay alguna forma de evitar esto?

Además, ahora devuelve un objeto. ¿Estoy en lo cierto al suponer que el valor de retorno es esencialmente this.setState?

A continuación se muestra un ejemplo que encontré en línea: Estado derivado de accesorios / estado .

antes de

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

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

Después

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;
  }
}
Andrés
fuente

Respuestas:

96

Acerca de la eliminación de componentWillReceiveProps: debe poder manejar sus usos con una combinación de getDerivedStateFromPropsy componentDidUpdate, vea la publicación del blog React, por ejemplo, migraciones. Y sí, el objeto devuelto por getDerivedStateFromPropsactualiza el estado de manera similar a un objeto pasado setState.

En caso de que realmente necesite el valor anterior de un accesorio, siempre puede almacenarlo en caché en su estado con algo como esto:

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

Cualquier cosa que no afecte el estado se puede poner componentDidUpdate, e incluso hay una getSnapshotBeforeUpdatepara cosas de muy bajo nivel.

ACTUALIZACIÓN: para tener una idea de los métodos de ciclo de vida nuevos (y antiguos), el paquete react-lifecycle-visualizer puede ser útil.

Oblosys
fuente
1
Ugh, estropeé la pregunta. En realidad quise decircomponentWillReceiveProps
Andrew
2
Había pensado en usar mi estado para mantener accesorios anteriores, pero realmente quería evitar el código y la lógica adicionales necesarios para implementarlo. Investigaré algunas de las otras cosas que mencionas. ¡Muchas gracias!
Andrew
44
Tener que almacenar un accesorio anterior en un estado es solo una solución alternativa para este cambio de API de React difícil de entender. Para los ojos de muchos desarrolladores, esto parece un antipatrón y un cambio de regresión. No te critico a Oblosys, sino al equipo React.
AxeEffect
2
@AxeEffect Esto se debe a que getDerivedStateFromPropsnunca fue realmente destinado a la memorización . Consulte mi respuesta a continuación, donde describí el enfoque recomendado .
Dan Abramov
¿Es eso un error tipográfico? ¿Te perdiste ...? Es decir, si devolvemos el objeto de estado completo o solo la parte que nos interesa.
theprogrammer
51

A medida que recientemente publicado en el blog de React , en la gran mayoría de los casos no se necesita getDerivedStateFromPropsen absoluto .

Si solo desea calcular algunos datos derivados, ya sea:

  1. Hazlo bien adentro render
  2. O, si volver a calcularlo es costoso, use un asistente de memorización como memoize-one.

Aquí está el ejemplo más simple "después":

import memoize from "memoize-one";

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

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

Consulte esta sección de la publicación del blog para obtener más información.

Dan Abramov
fuente
45
Si no se necesita en la gran mayoría de los casos, me sorprende que haya sido un cambio tan necesario, que romperá miles de proyectos en funcionamiento. Parece que el equipo de React comenzó con la ingeniería.
Ska
39
Cambie de componentWillReceiveProps a getDerivedStateFromProps. No se rompe sino que obliga a refactorizar todo el código existente, lo que lleva mucho tiempo. Y parece muy poco beneficioso ya que dice que no debería usarlo en la gran mayoría de los casos. ¿Por qué pasar por la molestia de cambiar la API por algo que ni siquiera debería usarse en primer lugar?
Ska
44
Me encantaría recibir una respuesta a este comentario de Dan Abramov.
Louis345
66
@DanAbramov ¿alguna respuesta sobre por qué se produjo este cambio?
Petros Kyriakou
3
En realidad en nuestros proyectos esto se usa mucho. Para mostrar cosas como Snackbars en pantallas para cuando bajan nuevos datos, 1 ejemplo. componentWillReceivePropsfue simple y funcionó. ¿Por qué eliminarlo para esta basura estática ...
Oliver Dixon
6

Como lo menciona Dan Abramov

Hazlo dentro del render

De hecho, utilizamos ese enfoque con la primera memoria para cualquier tipo de accesorios de proxy para cálculos de estado.

Nuestro código se ve de esta manera

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

Los beneficios de esto son que no necesita codificar toneladas de repeticiones de comparación dentro getDerivedStateFromPropsocomponentWillReceiveProps puede omitir la inicialización de copiar y pegar dentro de un constructor.

NOTA:

Este enfoque se usa solo para representar los accesorios para que se declaren, en caso de que tenga alguna lógica de estado interno, aún debe manejarse en los ciclos de vida de los componentes.

mpospelov
fuente