React: componentDidReceiveProps Por favor

Creado en 27 feb. 2015  ·  94Comentarios  ·  Fuente: facebook/react

Me gustaría solicitar humildemente un gancho componentDidReceiveProps, muchas veces me gustaría hacer algo tanto en componentWillMount como en componentWillReceiveProps, pero debido a que this.props aún no se ha configurado, me veo obligado a pasar accesorios en lugar de leer directamente desde this.props .

Antes de New Hook

componentWillMount() {
  this.setup(this.props.id);
}

componentWillReceiveProps(next) {
  this.setup(next.id);
}

setup(id) {
  UserActions.load(id);
}

Después de New Hook

componentWillMount() {
  this.setup();
}

componentDidReceiveProps() {
  this.setup();
}

setup() {
  UserActions.load(this.props.id);
}

En este ejemplo simple puede parecer una cosa pequeña, pero muchas veces el paso de accesorios es profundo y en lugar de hacer referencia conveniente a this.props, uno se ve obligado a sondear los accesorios en todo el componente.

Considere agregar componentDidReceiveProps como un gancho para aprovechar el mismo código que se aprovecha en componentWillMount sin forzar a ambos a colocar accesorios en todo el componente.

Comentario más útil

@syranide El problema es cuando el programa de instalación necesita llamar a métodos que también necesitan accesorios, que necesita llamar a métodos que también necesitan accesorios ... Eventualmente, todo su componente está buscando accesorios.

Todos 94 comentarios

¿Por qué no setup(props) ?

Al revisar mis propios proyectos, puedo ver que hice esto donde tenía necesidades similares (otro es derivar una parte del estado en función de los accesorios), simplemente pasando un conjunto diferente de accesorios cuando sea necesario para que solo tenga que pasar algo adicional al reutilizar, sin duplicar el conocimiento de qué accesorios se necesitan:

setup(props) {
  props = props || this.props
  UserActions.load(props.id)
}

@syranide El problema es cuando el programa de instalación necesita llamar a métodos que también necesitan accesorios, que necesita llamar a métodos que también necesitan accesorios ... Eventualmente, todo su componente está buscando accesorios.

+1 parece un montón de cableado innecesario en la aplicación con el patrón actual. Podría ser una forma concisa y estandarizada de resolver el problema. He visto a un montón de personas quemarse con this.props dentro de ComponentWillReceiveProps, una clara señal de que no es intuitivo.

+1, también lo encuentro frustrante.

Ya no creo que esto sea un problema.

+1, también me encuentro pasando mucho accesorios. Similar a @insin arriba, estuve usando parámetros predeterminados por un tiempo:

setup(props = this.props) {
  doSomething(props);
}

Pero decidió que era un anti-patrón debido a los errores sutiles que puede causar si olvida pasar newProps.

+1

Creo que la razón por la que no está disponible es porque this.props y this.state _siempre_ corresponden a valores renderizados. No hay forma de implementar componentDidReceiveProps que no active otro render sin romper esta restricción.

La mayoría de las veces, si está usando componentWillReceiveProps , en realidad desea un componente de orden superior a la Relay , o algo como observe hook propuesto para React 0.14 .

+1
Además, puede implementar componentDidReceiveProps sin cambiar this.props o this.state . Si todo lo que necesita hacer es leerlos, no activará otro renderizado. Si está escribiendo a accesorios o estado dentro de esta llamada de función propuesta, entonces se está disparando en el pie, pero ese es el mismo caso para todos los demás métodos en el ciclo de vida.

+1

Quiero poder responder al nuevo evento de accesorios cuando shouldComponentUpdate devuelva false , así que en mi caso no puedo usar componentDidUpdate .

+1

+1

¿Qué pasa con componentWillMount y componentWillUpdate @iammerrick

Quiero poder responder al nuevo evento de accesorios cuando shouldComponentUpdate devuelva falso, por lo que en mi caso no puedo usar componentDidUpdate.

Utilice componentWillReceiveProps ?

+1

+1 esto ayuda con el código DRY y simplificando la lógica.

Estaría abierto para que se llame a componentDidReceiveProps en la configuración inicial para hacer que el ejemplo que comienza este hilo sea una sola función:

componentDidReceiveProps() {
  UserActions.load(this.props.id);
}

¿Pensamientos?

componentWillReceiveProps() {
  setTimeout(()=> {
    if(this.isMounted()) {
      this.componentDidReceiveProps();
    }
  });
}
componentDidReceiveProps() {
  UserActions.load(this.props.id);
}

¿Qué piensas?

@YourDeveloperFriend Creo que depende de cómo funciona en relación con otros ganchos del ciclo de vida y retrasos en la representación debido a los tiempos de espera en cascada en los componentes anidados.

Estos tipos de enlaces de ciclo de vida deben llamarse sincrónicamente en una pasada que se garantiza que se llamará antes de la renderización. No he estudiado el código base de React, pero supongo que no se llama al renderizado en un tiempo de espera.

Lo más probable es que la mejor solución sea algo parecido a registrar ganchos de pre-renderizado o marcar componentes a los que se les ha cambiado sus accesorios para que la lógica de renderizado llame a componentDidReceiveProps antes de llamar a render.

+1 por favor. Tan feo.

No, estoy bien. Hay mejores soluciones.

+1

componentDidReceiveProps() ya existe: se llama render() . Sin embargo, puede haber un caso (como en el ejemplo de @iammerrick ) en el que necesite cargar / activar / etc algo antes de realizar un render. Esto significa una y la única cosa: haces algo mal.

Cuando haces cosas como

setup(id) {
    UserActions.load(id);
}

introduce statefullness en el componente o (mucho peor) en el exterior. ¿Por qué alguna vez necesitas load() datos cada vez que un componente recibe accesorios (ni siquiera se garantiza que sean nuevos )? Si realiza una carga diferida, la forma correcta es solicitar los datos en el método render() :

render() {
    var actions = UserActions.load(id);
    if (actions) // render
    else // show spinner or return null, etc.
}

UserActions.load = function(id) {
    if (data) return data;
    else // request the data, emit change on success
}

@robyoder , pasar argumentos a funciones no es feo. Puede parecer demasiado detallado, pero es natural en el lenguaje de programación que ha elegido. Destrozar la API agregando un método de ciclo de vida de repuesto solo para reducir la verbosidad es definitivamente feo.

Entonces dime, @ me-andre, ¿por qué tenemos componentWillUpdate y componentWillReceiveProps ?

En mi opinión, es porque sirven para diferentes propósitos y ambos son útiles. Así como esos dos no son lo mismo, render no es lo mismo que el hipotético componentDidReceiveProps porque se llama por razones distintas a los nuevos accesorios; además, no tienes acceso a los accesorios anteriores.

El beneficio de los accesorios anteriores es que no tendría que cargar datos cada vez; puede ser condicional según los accesorios y si han cambiado.

Obviamente, "[la fealdad] está en el ojo del espectador", porque agregar un método de ciclo de vida adecuado me parece una solución mucho más limpia.

Tal vez haya otra forma de ver esto ...

Digamos que los principales objetivos aquí son:

(1) reducir el error humano: olvidé pasar parámetros o usar props = props || this.props una vez más
(2) para reducir el código repetitivo que no agrega valor: por qué escribir algo innecesariamente
(3) para reducir la carga cognitiva de tratar de decidir qué métodos de ciclo de vida usar: por qué necesito pensar en este tipo de cosas, por qué sigo usando enfoques ligeramente diferentes dependiendo de cómo me sienta ese día, etc.

Entonces, tal vez el espacio de la solución sea simplificar tanto el uso de React como la API de React para lograr estos objetivos.

Si piensa en el problema con estos objetivos en mente, tal vez algunos métodos de ciclo de vida podrían fusionarse y si está interesado en saber que es inicialización vs actualización, puede saberlo por la firma / argumentos de llamada. Por ejemplo: beforeRender(prevProps, prevState) o update(prevProps, prevState) .

Personalmente, parece que hay demasiados métodos de ciclo de vida y si se llamaran de manera más consistente (sin o con prevProps / prevState en el paso inicial y actualización), podría simplificar mi código y aumentar mi productividad. Además, los nombres de los métodos del ciclo de vida son bastante largos (tiendo a copiarlos / pegarlos en lugar de escribirlos) y es difícil recordar cuáles existirán / existieron, lo que me hace pensar que podrían / ​​deberían simplificarse.

@robyoder , la respuesta corta por la que tenemos componentWillUpdate y componentDidReceiveProps es que hay una gran diferencia entre ellos. Son similares, por supuesto, pero principalmente porque tienen el prefijo componentWill .

| | estado ha cambiado | componente no se actualizó |
| --- | --- | --- |
| componentWillUpdate () | si | no |
| componentWillReceiveProps () | no | si |

Como habrá notado, hay puntos / condiciones del ciclo de vida en los que se llama a un método y no al otro. Es por eso que tenemos 2 métodos distintos de ciclo de vida.

Sin embargo, componentDidReceiveProps() como se describió en este tema no representa ningún estado o condición de componente y no da acceso a nada que componentWillReceiveProps() no. Simplemente agrega un ligero azúcar sintáctico que puede o no parecer útil o más fácil; esto es una preferencia personal, no un requisito técnico . Y esa es exactamente la razón por la que no debería agregarse: es subjetivo. Dices que pasar argumentos es feo, pero también dijiste "[la fealdad] está en el ojo del espectador". Para mí, parece que te has respondido a ti mismo.

@ me-andre, es posible que me esté respondiendo (@kmalakoff) y @robyoder al mismo tiempo. O tal vez no, pero aprovecharé esta oportunidad para hacer avanzar la discusión ...

Lo que estaba planteando era que quizás pensar por encima de la API actual con estos tres objetivos anteriores podría brindar nuevos conocimientos o perspectivas sobre cómo se podría simplificar la API para lograrlos. Por supuesto, después de realizar el ejercicio, podríamos terminar en el mismo lugar.

Repasemos el ejercicio ...

Supongamos que se sigue y se hace +1 en este tema porque hay algo importante en él y digamos que la adición de componentDidReceiveProps no es una buena idea porque aumenta el área de superficie de la API.

Teniendo en cuenta la tabla y los 3 objetivos que propongo, ¿alguien tiene alguna idea para simplificar la API y / u otra forma de dar a las personas en este hilo lo que desean y no expandir la API (tal vez incluso reducirla / simplificarla? )?

@kmalakoff , los 3 puntos de los que habla están conectados con la API solo en el sentido de que usa la API cuando los encuentra. No son causados ​​por un mal diseño.

El primer problema del que habla es que los métodos del ciclo de vida toman diferentes argumentos. Bueno, eso es perfectamente natural, sirven para diferentes propósitos. componentWillReceiveProps acepta accesorios no porque la luna estaba llena cuando se agregó este método, sino porque se trata de recibir accesorios que aún no se han asignado al componente. props se pasan solo en métodos donde (pueden) diferir de this.props . En realidad, esto es una pista, no un problema.
El problema n. ° 3 es que le resulta difícil decidir qué devolución de llamada / enfoque utilizar. Bueno, eso no es realmente un problema hasta que los métodos de los que hablo anteriormente tengan la misma firma. Una vez que tenga state y this.state , props y this.props que son iguales (leídos sin sentido) en la mayoría de los métodos, entonces se enreda en opciones de repuesto y alternativas.
El problema n. ° 2 tiene que ver con el código repetitivo ... bueno, React es una biblioteca delgada, y es hermosa, fácil y estricta por eso. Si desea hacer un contenedor que simplificaría nuestras vidas y reduciría la cantidad de código que escribimos todos los días, ¿por qué no hacerlo? Publícalo como tu propio npm que depende de react y listo.

Con respecto a "hay demasiados métodos de ciclo de vida", todavía no y nunca lo será si deja de solicitar nuevas devoluciones de llamada para las que no tiene una necesidad técnica. La necesidad técnica es cuando implementas un componente complejo y en medio del trabajo entiendes que no hay absolutamente ninguna forma de hacerlo sin algún truco feo. Se trata de "activar un método DOM complicado. Necesito que se llame a algo en el momento en que el componente hace X, pero no existe tal método y no puedo agregar uno, así que uso setTimeout y espero que no llegue antes de lo necesario". No cuando dices "oh, me cansé de escribir props = this.props".

Y una cosa más...
En una aplicación React bien diseñada, no necesita la mayoría de los métodos de los que hablamos o los usa muy raramente. getInitialState , componentWillMount , componentWillUnmount son suficientes el 99% del tiempo. En mi proyecto actual, que es una aplicación comercial relativamente grande, componentWillReceiveProps se usa dos veces en toda la aplicación. No lo usaría en absoluto, pero el entorno (leer el navegador) es imperfecto: manipula ciertas cosas "con estado en sí mismo" como scrollTop o confiando en cálculos de diseño para futuros render s requiere sincronización manual entre props transiciones y cambios DOM. Sin embargo, en la mayoría de los casos "normales", no es necesario saber cuándo ocurre la transición props .

@ me-andre basado en mi experiencia trabajando con React, creo que hay espacio para mejorar / simplificar la API. No estoy solo, por eso se planteó este problema en primer lugar y se está haciendo +1.

Si respondo proporcionando comentarios sobre su análisis, no creo que esto realmente avance (por eso he evitado hacerlo hasta ahora) ya que está un poco sesgado hacia el status quo y justifica la API actual, pero ¡Estaré encantado de proporcionar comentarios sobre posibles soluciones! Las personas involucradas en este problema buscan explorar ideas de mejora y posibles cambios en la API para abordar los problemas planteados a través de su experiencia con React (como se describió anteriormente).

Tal vez tenga razón en que la comunidad debería hacer la innovación para hacer avanzar la API y tal vez revertirla al núcleo o tal vez la API actual sea perfecta tal como está, pero creo que esta es una gran oportunidad para considerar cómo se vería el cambio con el equipo de React.

Tal vez la razón por la que está presentando argumentos como lo hace es porque solo somos usuarios de la biblioteca con menos comprensión de la API actual y las decisiones que se tomaron. Tal vez podría atraer a otros miembros del equipo de React que tengan la misma experiencia y conocimientos que usted para ver si obtenemos una perspectiva diferente y tal vez incluso se nos ocurra una gran solución o para llegar a un consenso de que esto es tan bueno como es posible.

@kmalakoff , imagina que React es un motor y 4 ruedas. Ahora, cada vez que quieres ir a alguna parte, construyes el resto del auto y eso eventualmente comienza a ser molesto. De lo contrario, se queja de la necesidad de recordar los detalles del motor de bajo nivel y de girar las ruedas delanteras con las manos no se siente de la manera correcta.
Lo que recomendaría es obtener un vehículo completo (un marco completo) o construir los componentes necesarios de manera que pueda reutilizarlos con el tiempo.
Lo que veo en este hilo es que los usuarios del motor se quejan de que el motor no tiene sistema hidráulico ni luces interiores. Lo que siento es que obtendríamos una completa mierda si agregamos estas características a donde no pertenecen.
React no es un marco: es más un motor de renderizado impulsado por diferencias que expone un potente sistema de componentes. Tiene una API minimalista y de bajo nivel, pero lo suficientemente potente como para permitirle construir literalmente cualquier cosa sobre ella.
No dude en ponerse en contacto conmigo por correo electrónico si cree que necesito aclarar algo; no quiero que este hilo se convierta en un tutorial.

@ me-andre Creo que ahora entendemos bien su posición y su línea de argumentación. ¡Gracias! Puede que tenga razón en que la API ya es tan buena como puede ser, pero también existe la posibilidad de que se pueda mejorar.

¿Alguien puede defender el cambio de API? p.ej. proporcionando un análisis comparativo de varias opciones (agregar componentDidReceiveProps vs fusionar / simplificar APIs vs sin cambios)

El equipo de React ha estado observando el problema y lo discutimos internamente. En principio, siempre me inclino por eliminar el área de superficie de la API, por lo que me inclino hacia los argumentos de @ me-andre. Sin embargo, nuestro equipo a veces se refiere afectuosamente a componentDidReceiveProps como "el método del ciclo de vida que falta", y hay serias discusiones sobre la posibilidad de agregarlo. Lo importante es que habilitamos las mejores prácticas sin catalizar las malas prácticas.

Lo que sería más útil para nosotros (o al menos para mí) es tener una comprensión sólida de POR QUÉ la gente quiere este método de ciclo de vida. ¿Qué está tratando de hacer en este ciclo de vida que no esté suficientemente cubierto por los otros métodos del ciclo de vida? ¿Por qué alguien querría hacer UserActions.load(id); dentro de componentDidReceiveProps lugar de dentro del render como se sugiere en https://github.com/facebook/react/issues/3279#issuecomment -163875454 (puedo pensar en una razón, pero tengo curiosidad por saber cuáles son tus razones)? ¿Hay otros casos de uso además de iniciar una carga de datos basada en accesorios?

@jimfb Creo que componentDidReceiveProps es exactamente lo que necesito y que otros métodos son inapropiados, pero podría estar equivocado. Con mucho gusto explicaré mi caso de uso.

Tengo una ruta en mi react-router que se ve así:

    <Route path="/profile/:username" component={ProfilePage} />

Necesito obtener los datos del perfil de una fuente externa y, para hacerlo correctamente, necesito realizar una solicitud HTTP en el método componentDidMount .

Desafortunadamente, cuando navego de un perfil a otro, React Router no vuelve a llamar al método constructor o al método componentDidMount (por supuesto, esto podría ser un error, pero voy a asumir que no lo es por ahora). .

Pensé en arreglar esto usando el componentDidReceiveProps teórico. Lamentablemente, aún no existe y componentWillReceiveProps no satisfará mis necesidades.

Cualquier sugerencia sobre esto sería muy apreciada.

Mi corazonada es que componentDidReceiveProps es exactamente lo que necesito.

@ shea256 ¿Puede explicar por qué componentWillReceiveProps no satisface sus necesidades?

@ shea256 Además, ¿por qué su componente necesita realizar una solicitud HTTP? ¿Qué contiene esa solicitud http? Si se trata de datos, ¿por qué no actualiza su componente de forma asincrónica cuando regresa la devolución de llamada de datos?

@jimfb El caso común para mí es cuando tenemos que cargar algo de forma asincrónica en respuesta a un cambio de prop, lo que implica establecer algún estado para indicar que se está cargando y luego, cuando se cargan los datos, establecer ese estado.

Tanto el montaje como la recepción de nuevos accesorios deberían activar este mismo ciclo de carga, por lo que componentDidMount y componentWillReceiveProps es el lugar para llamar a la función de actualización. render no tiene información sobre si es un nuevo accesorio y, de todos modos, setState from render es un no-no.

Entonces, tengo una función que hace la carga. Pero tengo que pasar props como parámetro y evitar cuidadosamente el uso de this.props que están desactualizados por componentWillReceiveProps . Esto inevitablemente termina causando errores, ya que debe recordar usar los accesorios pasados ​​o obtendrá errores sutiles uno detrás cuando lleguen los cambios. Y las firmas de todos los métodos involucrados son más complejas ya que los accesorios deben pasarse.

Sí, se puede hacer con la API actual. Pero causa un código más torpe y propenso a errores, que es lo que React es tan bueno para evitar en general.

@jimfb Necesito obtener el nuevo nombre de usuario en routeParam y con componentWillReceiveProps todavía no lo tengo.

Necesito hacer una solicitud HTTP para cargar los datos del perfil desde una fuente externa (los datos no se almacenan localmente).

Y puedo actualizar mi componente en el método de renderizado, pero se siente un poco sucio:

constructor(props) {
  super(props)

  this.state = {
    id: this.props.routeParams.id,
    profile: {}
  }
}

componentDidMount() {
  this.getProfile(this.state.id)
}

render() {
  if (this.props.routeParams.id !== this.state.id) {
    this.getProfile(this.props.routeParams.id)
  }

  return (
    <div>
      <div className="name">
       {this.state.profile.name}
      </div>
    </div>
  )
}

@grassick escribió:

El caso común para mí es cuando tenemos que cargar algo de forma asincrónica en respuesta a un cambio de prop, lo que implica establecer algún estado para indicar que se está cargando y luego, cuando se cargan los datos, establecer ese estado.

Sí, este es exactamente mi caso de uso.

Tanto el montaje como la recepción de nuevos accesorios deberían activar este mismo ciclo de carga, por lo que componentDidMount y componentWillReceiveProps es el lugar para llamar a la función de actualización.

Bien dicho.

@ shea256 ¿no se puede hacer esto usando componentWillReceiveProps lugar?

constructor(props) {
  super(props)

  this.state = {
    id: this.props.routeParams.id,
    profile: {}
  }
}

componentDidMount() {
  this.getProfile(this.state.id)
}

componentWillReceiveProps(nextProps) {
  let { id } = nextProps.params
  if(id !== this.state.id) {
    this.getProfile(id, (profile) => {
      this.setState({ id: id, profile: profile })
    })
  }
}

render() {
  return (
    <div>
      <div className="name">
       {this.state.profile.name}
      </div>
    </div>
  )
}

@grassick escribió:

El caso común para mí es cuando tenemos que cargar algo de forma asincrónica en respuesta a un cambio de prop, lo que implica establecer algún estado para indicar que se está cargando y luego, cuando se cargan los datos, establecer ese estado.

@grassick escribió:

render no tiene información sobre si es un nuevo accesorio y, de todos modos, setState from render es un no-no.

Solo escupir aquí: si componentDidUpdate se invocara en el renderizado inicial (además de los renderizados posteriores), ¿eso resolvería su caso de uso? Puede verificar si los accesorios cambiaron y hacer que todos sus datos se carguen en componentDidUpdate , ¿verdad? Y en render, sabría que estaba cargando datos si this.state.profileName != this.props.profileName . ¿Esa alternativa sería suficiente para sus casos de uso?


¿Existe algún caso de uso que la gente tenga que NO involucre componentes que carguen datos basados ​​en accesorios?

@calmdev hm, creo que tienes razón. Lo intentaré.

@jimfb quizás, aunque intenté usar componentDidUpdate y pensé que no funcionaba. Puedo echar otro vistazo.

Y parece que podría comprobar totalmente this.state.profileName != this.props.profileName pero esto también parece un truco, ¿no? En este punto, si componentWillReceiveProps(nextProps) termina funcionando, preferiría eso. Ahora, lo que me molesta es la falta de simetría con componentDidMount . ¿Puedo usar componentWillMount en lugar de componentDidMount ?

Siento que todo el ciclo de vida podría ser más limpio.

@ shea256 , tengo una pregunta para ti ... ¿has leído el README para React?
No me siento cómodo diciendo eso, pero si no es así, posiblemente no debería solicitar nuevas funciones para la herramienta con la que no está familiarizado. Para mí eso incluso suena ... absurdo.
"¿Puedo usar componentWillMount en lugar de componentDidMount ?"
No, no puede porque, como se indica en el archivo Léame, se llama a componentWillMount antes de que su componente llegue al DOM y componentDidMount - después de eso. Bueno, definitivamente puedes reemplazar un método por otro y eso rompería tu código. La razón por la que tenemos 2 métodos aquí no es la estética (lea "simetría"). Es porque es posible que necesitemos un método para hacer una preparación antes de renderizar y el otro para algunas consultas / manipulaciones DOM iniciales después del primer renderizado. Pero incluso eso es exótico para un componente React promedio. Normalmente, no es necesario acceder al DOM manualmente.
"Y parece que podría hacer una comprobación de this.state.profileName! = This.props.profileName, pero esto también parece un truco, ¿no?"
Sí, todo tu enfoque es un truco. ¿Y quién te dijo que componentDidReceiveProps garantizaría que los props se hayan cambiado? Nada hará eso a menos que defina shouldComponentUpdate .
Así es como funciona React.

@ yo-andre, gracias por participar, pero estás siendo un poco abrasivo para mi gusto. Además, no creo que haya entendido mis preguntas. Estoy bastante familiarizado con lo que hacen componentWillMount y componentDidMount . Esperaré la respuesta de

@ shea256 ,
"Además, no creo que hayas entendido mis preguntas".
¿Podría señalar dónde me equivoco cuando respondo a sus preguntas?
Además, ¿podría aclarar lo que está tratando de preguntar?
Además, no estamos aquí para discutir personalidades o gustos. Esta no es una charla privada, ni es un soporte técnico. Este ni siquiera es un sitio web de intercambio de pila donde podría esperar que uno lo guíe a través de algún área de conocimiento.
A menudo hablo en reuniones locales y conferencias internacionales sobre React (y no solo) y estoy muy abierto a compartir conocimientos, cuando y donde sea apropiado. Siempre puedes contactarme directamente por correo electrónico o skype.

Con respecto a su problema con la carga de datos de perfil. Está tratando de resolver el problema de aplicar el enfoque imperativo clásico a un marco orientado a funciones. Una vista en React es una función de los accesorios, el estado y el entorno. Como function(state, props) { return // whatever you've computed from it } (pero las cosas se vuelven un poco más complejas en el mundo real; de lo contrario, React no existiría en absoluto). Aunque en 0.14 obtenemos componentes funcionales puros, para la mayoría de los componentes esta función de entrada es render() .

Significa que comienzas a escribir desde render() y asumes que tus accesorios están siempre actualizados y no te importa si props se han cambiado o no, cuántas veces y dónde . Su caso podría implementarse de la siguiente manera:

// profile-view.js

var React = require('react');

module.exports = React.createClass({
    contextTypes: {
        app: React.PropTypes.object.isRequired
        // another option is var app = require('app')
        // or even var profiles = require('stores/profiles')
    },
    componentWillMount() {
        var {app} = this.context; // another option is to require('app')
        app.profiles.addListener('change', this.onStoreChange);
    },
    componentWillUnmount() {
        var {app} = this.context; // another option is to require('app')
        app.profiles.removeChangeListener('change', this.onStoreChange);
    },
    onStoreChange() {
        this.forceUpdate();
    },
    render() {
        var {app} = this.context;
        var profile = app.profiles.read(this.props.routeParams.id);
        if (profile) { // profile's been loaded
            return <div>{profile.name}</div>;
        } else { // not yet
            return <div>Loading...</div>;
        }
    }
});

// profiles-store.js
// app.profiles = new Profiles() on app initialization

var EventEmitter = require('events');
var {inherits} = require('util');

module.exports = Profiles;

function Profiles() {
    this.profiles = {};
}

inherits(Profiles, EventEmitter);

Profiles.prototype.read = function(id) {
    var profile = this.profiles[id];
    if (!profile) {
        $.get(`profiles/${id}`).then(profile => {
            this.profiles[id] = profile;
            this.emit('change');
        });
    }
    return profile;
};

Bastante simple.
Y no necesitas componentDidReceiveProps . Ni siquiera necesitas componentWillReceiveProps y ganchos similares. Si alguna vez siente que los necesita para un caso trivial, no comprende cómo hacer las cosas en React. Para obtenerlo , utilice los recursos adecuados, no se limite a eliminar la sección Problemas del repositorio . Se siente un poco abrasivo para mi gusto. Incluso parece que no respetas el tiempo de los demás.

@ me-andre Probablemente valga la pena bajar un poco el tono de tu lenguaje, ya que puede parecer un poco conflictivo aunque solo estés tratando de ayudar. Queremos crear una comunidad acogedora para todos; todos éramos novatos en algún momento. A pesar de que tiene razón sobre algunos de sus puntos en la API / diseño, el hecho de que tanta gente esté haciendo +1 en el problema es una indicación de que algo anda mal, por lo que deberíamos tratar de entender qué / por qué la gente quiere esto. ciclo vital. Tal vez el problema sea que no estamos comunicando suficientemente cómo escribir correctamente los componentes (documentación), o tal vez la API de React necesita un cambio; de cualquier manera, vale la pena comprender las quejas / preguntas / comentarios aquí.

@ shea256 this.state.profileName != this.props.profileName está comprobando si el estado interno (lo que representa el componente) coincide con lo que el padre le pide al componente que renderice. Esta es casi la definición de "el componente se está actualizando" o "el componente está actualizado", por lo que no lo veo como un truco. Al menos, no es más hacky que la idea de "disparar una solicitud de actualización de datos cuando la propiedad cambia y hacer un setstate en la devolución de llamada" está en primer lugar.

@ shea256 Para ser claros: este ciclo de vida propuesto no le permitiría hacer nada que no pudiera hacer hoy con los ciclos de vida actuales. Simplemente lo haría potencialmente más conveniente. Como han mencionado otros, lo que estás intentando hacer es posible con los ciclos de vida actuales (existen múltiples combinaciones de ciclos de vida que te permitirían alcanzar tu objetivo). Si está tratando de "hacer que funcione", entonces StackOverflow sería un mejor lugar para discutir. Este problema de github intenta comprender la necesidad de componentDidReceiveProps en lo que respecta a la ergonomía del desarrollador.

¿Y quién te dijo que componentDidReceiveProps garantizaría que los accesorios se hayan cambiado?

@ me-andre tiene razón aquí. La mayoría de los métodos del ciclo de vida en realidad no garantizan que algo haya cambiado; sólo indican que algo _podría_ haber cambiado. Por esta razón, siempre debe verificar si previous === next si va a hacer algo como hacer una solicitud http. Un grupo de personas en este hilo parece estar asumiendo que su valor ha cambiado simplemente porque se activó el método del ciclo de vida.

@ yo-andre escribió:

Está tratando de resolver el problema de aplicar el enfoque imperativo clásico a un marco orientado a funciones.

Creo que esta podría ser la causa principal de que las personas quieran este nuevo método de ciclo de vida, pero todavía estoy tratando de averiguar por qué / cómo las personas quieren usar este método. Es muy posible que la semántica del ciclo de vida actual esté un poco mal alineada con lo que la gente normalmente quiere hacer.

Todos: ¿Hay algún caso de uso para componentDidReceiveProps no sea "cargar datos de forma asincrónica en respuesta a un cambio de prop"?

cc @grassick @iammerrick

@jimfb Busqué mi código y también uso componentWillReceiveProps para construir objetos costosos de crear que se necesitan para renderizar. Este caso tiene el mismo problema que tiene que pasar props y tener cuidado de no usar this.props en el código.

@jimfb escribió:

@ me-andre Probablemente valga la pena bajar un poco el tono de tu lenguaje, ya que puede parecer un poco conflictivo aunque solo estés tratando de ayudar. Queremos crear una comunidad acogedora para todos; todos éramos novatos en algún momento. A pesar de que tiene razón sobre algunos de sus puntos en la API / diseño, el hecho de que tanta gente esté haciendo +1 en el problema es una indicación de que algo anda mal, por lo que deberíamos tratar de entender qué / por qué la gente quiere esto. ciclo vital. Tal vez el problema sea que no estamos comunicando suficientemente cómo escribir correctamente los componentes (documentación), o tal vez la API de React necesita un cambio; de cualquier manera, vale la pena comprender las quejas / preguntas / comentarios aquí.

Gracias por mencionar esto. Es muy importante asegurarse de que tenga una comunidad acogedora y que mis comunicaciones con usted solo hayan sido agradables.

@jimfb escribió:

@ shea256 this.state.profileName! = this.props.profileName está comprobando si el estado interno (lo que está representando el componente) coincide con lo que el padre le pide al componente que renderice. Esta es casi la definición de "el componente se está actualizando" o "el componente está actualizado", por lo que no lo veo como un truco. Al menos, no es más hacky que la idea de "disparar una solicitud de actualización de datos cuando la propiedad cambia y hacer un setstate en la devolución de llamada" está en primer lugar.

Sí, esto parece razonable.

@jimfb escribió:

@ shea256 Para ser claros: este ciclo de vida propuesto no le permitiría hacer nada que no pudiera hacer hoy con los ciclos de vida actuales. Simplemente lo haría potencialmente más conveniente. Como han mencionado otros, lo que estás intentando hacer es posible con los ciclos de vida actuales (existen múltiples combinaciones de ciclos de vida que te permitirían alcanzar tu objetivo). Si está tratando de "hacer que funcione", entonces StackOverflow sería un mejor lugar para discutir. Este problema de github intenta comprender la necesidad de componentDidReceiveProps en lo que respecta a la ergonomía del desarrollador.

Yo estoy muy de acuerdo con esto. No publiqué mi caso de uso particular para recibir ayuda. Publiqué para proporcionar una idea de por qué pensé que componentDidReceiveProps sería útil.

Yo y más de una docena de personas que publicaron publicaciones claramente teníamos el instinto de usar algo como esto (lo que significa que probablemente hay cientos o miles más), lo que me indica que la API no es tan intuitiva como podría ser.

@jimfb escribió:

@ me-andre tiene razón aquí. La mayoría de los métodos del ciclo de vida en realidad no garantizan que algo haya cambiado; solo indican que algo podría haber cambiado. Por esta razón, siempre debe verificar si anterior === siguiente si va a hacer algo como hacer una solicitud http. Un grupo de personas en este hilo parece estar asumiendo que su valor ha cambiado simplemente porque se activó el método del ciclo de vida.

No estoy haciendo esta suposición. Dejé fuera el cheque, pero ahora estoy usando uno. Pero en el peor de los casos, se activaría una solicitud adicional.

@jimfb escribió:

Creo que esta podría ser la causa principal de que las personas quieran este nuevo método de ciclo de vida, pero todavía estoy tratando de averiguar por qué / cómo las personas quieren usar este método. Es muy posible que la semántica del ciclo de vida actual esté un poco mal alineada con lo que la gente normalmente quiere hacer.

Quizás. Pensaré más en esto.

componentDidReceiveProps sería útil para un caso de uso específico que tengo.

Estoy usando la arquitectura ReactRouter y Flux. Cuando se crea una instancia de mi componente, establezco mi estado en un artículo de una tienda. Cuando esa tienda emite un evento de cambio, actualizo mi estado usando la misma consulta de recuperación.

Cuando mis accesorios cambian porque navegué a un ID de artículo diferente en la misma ruta, necesito consultar mi tienda nuevamente o mi componente se bloqueará con el estado del artículo anterior en la tienda.

Actualmente, cuando se llama a componentWillReceiveProps , verifico si alguno de mis parámetros de ruta cambió y, si lo hicieron, llamo a updateItemState. Pero, debido a que los accesorios aún no han cambiado, ahora debo pasar los accesorios a mi método.

this.updateItemState( nextProps );

updateItemState( props ) {
    props = props || this.props;

    this.setState( {
        item: this.getItemState( props )
    } );
}

getItemState( props ) {
    props = props || this.props;

    return this.ItemStore.get( props.params[ this.paramName ] );
}

simplemente se convertiría en

this.updateItemState();

updateItemState() {
    this.setState( {
        item: this.getItemState()
    } );
}

getItemState() {
    return this.ItemStore.get( this.props.params[ this.paramName ] );
}

Siento que este es un caso de uso razonable.

@ akinnee-gl et al: Si componentDidUpdate se activara después del montaje inicial además de las actualizaciones, ¿resolvería eso su caso de uso?

@ akinnee-gl, si usa Flux, entonces no necesita establecer el estado de una tienda. A menos que sea absolutamente imposible, debe mantener el estado en un solo lugar. Cuando se trata de Flux, ese lugar es la propia tienda. Cuando la tienda emite un cambio, solo forceUpdate() y luego en render() usted read() su tienda.

"Cuando mis accesorios cambian porque navegué a un ID de artículo diferente en la misma ruta, necesito consultar mi tienda nuevamente o mi componente se bloqueará con el estado del artículo anterior en la tienda".

Nunca.

Eche un vistazo: necesita renderizar un artículo en particular de una tienda y qué artículo renderizar, decide entre props . Si ese es su requisito, debe expresarse en el código de la misma manera que en palabras:

    var item = this.props.store.read(this.props.id);
    return <div>{item.name}</div>;

Este es tu componente, nada más.
Para hacer este juego con Flux, puede escribir un componente reutilizable para la vinculación / desvinculación de la tienda:

<FluxWrapper store={store} component={Component} id={this.props.routeParams.id} />

var FluxWrapper = React.createClass({
    componentWillMount() {
        this.props.store.addListener('change', this.onStoreChange);
    },
    componentWillUnmount() {
        this.props.store.removeListener('change', this.onStoreChange);
    },
    onStoreChange() {
        this.forceUpdate();
    },
    render() {
        var Component = this.props.component;
        return <Component {...this.props} />;
    }
});

var Component = React.createClass({
    render() {
        var item = this.props.store.read(this.props.id);
        return <div>{item.name}</div>;
    }
});

Vea qué tan pequeños pueden volverse sus componentes si usa React correctamente.

Sin embargo, si sus tiendas son pesadas y no puede simplemente read() en cada render() , entonces necesita un middleware de almacenamiento en caché. Puede ser un componente reutilizable (similar a FluxWrapper ) o una tienda proxy intermedia que oculta el método read() . Las tiendas proxy son fáciles y geniales: no solo pueden almacenar en caché las lecturas, sino también suprimir los cambios si un cambio en la tienda principal no fue importante o significativo para el caso.
A esto se le llama composición. Debería preferir la composición a la herencia o extender la funcionalidad o lo que sea porque la composición escala .
Tan pronto como tenga un problema que sea difícil de resolver mediante el uso de un componente, considere usar 2 o más componentes en lugar de introducir complejidad a los componentes existentes.

@jimfb , con respecto a mi tono: no soy un hablante nativo de inglés y ni siquiera tengo una práctica regular de hablar, por lo que a veces puedo elegir las palabras equivocadas, simplemente no siento cómo suenan emocionalmente.

Enfoque interesante. Lo recordare. Todos los documentos oficiales de React dicen que debes mantener puras tus funciones de renderizado (lo que significa que solo debes acceder a los accesorios y el estado en los métodos de renderizado). Todos los ejemplos de Flux muestran el estado de configuración de la tienda.

@jimfb El problema con componentDidUpdate es que se llama por otro motivo que no sea el cambio de accesorios. Además, me obligaría a esperar hasta que el componente ya se haya actualizado, ¡luego llamar a setState para actualizarlo nuevamente! Parece bastante ineficiente.

@ akinnee-gl componentWillReceiveProps también se puede llamar por razones distintas al cambio de accesorios (consulte: https://github.com/facebook/react/issues/3279#issuecomment-164713518), por lo que DEBE verificar para ver si el accesorio realmente cambió de todos modos (independientemente del ciclo de vida que elija). Además, si realmente está haciendo datos asincrónicos, estará esperando el siguiente bucle de eventos antes de que se active su devolución de llamada de todos modos, por lo que no creo que importe si su función se invoca inmediatamente antes / después de su renderizar la función, ¿verdad?

@jimfb Bueno, en realidad, si el elemento al que hace referencia el accesorio ya está en la tienda, no es necesario volver a renderizarlo. Si aún no está en la tienda, es posible que deseemos mostrar una pantalla de carga. Entonces todavía no tiene sentido renderizar dos veces. Aunque veo lo que estás diciendo.

@ me-andre Me gusta mucho tu enfoque de simplemente leer fuera de la tienda durante el renderizado. Parece simplificar el código. Sin embargo, me preocupa que pierda algo de eficiencia al hacer eso. Pierde la capacidad de controlar la actualización a través de shouldComponentUpdate, y requiere que agregue ese componente FluxWrapper adicional que mencionó. ¿Alguna idea sobre eso?

He estado monitoreando la discusión y me gustaría resurgir la tercera opción: (1) la API es excelente tal como está, (2) necesita componentDidReceiveProps y (3) tal vez haya una API más simple.

Considero el problema como una oportunidad para ver si podemos resolver la necesidad subyacente que plantea este problema mediante una lluvia de ideas de cambios más profundos en la API en lugar de limitar la discusión al espacio de la solución de (1) y (2).

Una API simplificada podría basarse en los siguientes principios:

(A) simplifique la estructura del código / reduzca el texto estándar
(B) mantenga los accesorios (entradas) separados del estado (interno)

La razón principal de (A) es que me encuentro escribiendo un texto repetitivo que no agrega valor. Ejemplo:

componentWillMount() { this.actuallyCheckThePropsAndMaybeDoSomething(); }
componentWillReceiveProps(nextProps) { this.actuallyCheckThePropsAndMaybeDoSomething(nextProps); }

actuallyCheckThePropsAndMaybeDoSomething(props) {
  props = props || this.props;

  let relatedProps1 = _.pick(props, KEYS1);
  if (!shallowEqual(this.relatedProps1, relatedProps1) { // changed
   this.relatedProps1 = relatedProps1;

   // do something
  }

  let relatedProps2 = _.pick(props, KEYS2);
  if (!shallowEqual(this.relatedProps1, relatedProps2) { // changed
   this.relatedProps2 = relatedProps2;

   // do something else
  }
}

Realmente preferiría hacer lo siguiente y no tener una ruta de código separada por primera vez en lugar de cambiarla:

propsUpdated(prevProps) {
  if (!shallowEqual(_.pick(prevProps || {}, KEYS1), _.pick(this.props, KEYS1)) { // changed
   // do something
  }

  if (!shallowEqual(_.pick(prevProps || {}, KEYS2), _.pick(this.props, KEYS2)) { // changed
   // do something
  }
}

En cuanto a (B), el principal problema con el componentDidUpdate es como se acaba de mencionar es que puede activar llamadas de método repetidas si establece el estado debido a cambios de propiedad, ya que se llama tanto para props (entradas) como para el estado (interno). Estas rutas de código parecen estar mejor desacopladas porque los accesorios se suministran desde el exterior y el estado se establece en el interior, por ejemplo. He intentado / considerado una cuarta posibilidad de updated(prevProps, prevState) (un nombre simplificado para componentDidUpdate ya que menos métodos de ciclo de vida podrían permitir nombres más cortos) usando componentDidUpdate para reducir el texto repetitivo, pero me encuentro un poco insatisfecho con el segundo potencial redundante actualizar llamadas y que la lógica parece ser bastante independiente en la práctica.

Basado en comenzar con los principios de diseño (¡estoy seguro de que hay más!), Me pregunto si algo como propsUpdated y stateUpdated podría ser una posible tercera opción para esta discusión. ?

@kmalakoff Siempre necesitará verificar si los accesorios cambiaron, ya que nunca podremos decirle definitivamente que los accesorios cambiaron / no cambiaron (porque Javascript no tiene tipos de valor y tiene mutabilidad).

En esa nota, tal vez podría llamar a shouldComponentUpdate para decir si debe llamar a propsDidChange o lo que sea. Sin embargo, un problema separado.

@jimfb no sigue con precisión cuál es la limitación de idioma a la que se refiere y cómo se relaciona con la simplificación de la API en general. ¿Puede explicarlo con un poco más de detalle o con un ejemplo? Necesito esta explicación simplificada / expandida un poco para entender ...

Creo que quiere decir que no hay una forma rápida e incorporada de verificar si this.props === nextProps porque pueden ser o no dos objetos separados.

{ a: 1 } === { a: 1 } produce falso porque son dos objetos separados, pero

var a = { a: 1 };
var b = a;
a === b

da como resultado verdadero porque en realidad ambos son una referencia al mismo objeto.

Podríamos comprobar de forma recursiva la igualdad de cada propiedad, pero eso podría ser bastante lento. Es por eso que depende de nosotros implementar shouldComponentUpdate

@kmalakoff No quiero divagar en este hilo, pero aquí hay una esencia para ti: https://gist.github.com/jimfb/9ef9b46741efbb949744

TLDR: @ akinnee-gl es exactamente correcto en su explicación (¡Gracias!). Con la corrección menor de que no siempre podemos hacer la verificación recursiva de todos modos (incluso si quisiéramos, y el rendimiento no fuera un problema), porque no hay forma de verificar de forma recursiva una función de devolución de llamada pasada como un accesorio.

Sin embargo, mantengamos este hilo sobre el tema: P.

¡Gracias a los dos! Mmmm, todavía no está del todo claro por qué simplificar la API no es una opción para resolver este problema. Agregaré comentarios a la esencia ...

Si alguien quiere participar en una gama más amplia de soluciones, ¡únase a nosotros!

@jimfb @calmdev He probado tus sugerencias y ahora tengo una idea completa de por qué componentDidReceiveProps realmente no agrega nada nuevo y no debería agregarse como una función adicional. Si bien confiaba en su palabra antes, ahora tiene un sentido intuitivo para mí por qué este es el caso.

Para ilustrar cómo llegué a esta conclusión, me gustaría compartir mi código más reciente. Quizás esto también pueda ayudar a otros.

Eche un vistazo al componente de mi página de perfil:

class ProfilePage extends Component {
  static propTypes = {
    fetchCurrentProfile: PropTypes.func.isRequired,
    currentProfile: PropTypes.object.isRequired
  }

  constructor(props) {
    super(props)
    this.state = { currentProfile: {} }
  }

  componentHasNewRouteParams(routeParams) {
    this.props.fetchCurrentProfile(routeParams.id)
  }

  componentWillMount() {
    this.componentHasNewRouteParams(this.props.routeParams)
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.routeParams.id !== this.props.routeParams.id) {
      this.componentHasNewRouteParams(nextProps.routeParams)
    }
    this.setState({
      currentProfile: nextProps.currentProfile
    })
  }

  render() {
    var profile = this.state.currentProfile
    return (
      <div>
        <h1>{ profile.name ? profile.name : null }</h1>
      </div>
    )
  }
}

Cuando el componente está montado, no tiene datos de perfil y necesita cargar el perfil desde una fuente externa.

Entonces ... hago que llame a la función fetchCurrentProfile (envuelta en componentHasNewIdProp ) que obtiene los datos de la fuente externa, luego envía una acción de actualización a Redux. El reductor recibe esta acción y actualiza el estado de la aplicación, que luego actualiza los accesorios para el componente ProfilePage .

Ahora, dado que se han actualizado los accesorios, se llama a la función componentWillReceiveProps . Pero no conocemos el contexto del evento de cambio de prop, por lo que necesitamos averiguar qué props (si corresponde) se han cambiado. Vemos que el ID es el mismo, por lo que no es necesario volver a llamar a fetchCurrentProfile . Luego actualizamos state.currentProfile para que el componente sepa volver a renderizar con los nuevos datos de perfil (sí, podría hacer esto con accesorios, pero hay otra razón para esto en la que no quiero entrar aquí). En este momento, podríamos verificar si props.currentProfile ha cambiado antes de actualizar el estado, pero esta no es una operación costosa, por lo que no importa si lo verificamos.

Entonces ... digamos que queremos navegar de un perfil a otro. Hacemos clic en un enlace y el enrutador activa el cambio de ruta. Ahora tenemos nuevos parámetros de ruta y se activa la función componentWillReceiveProps . Vemos que id ha cambiado y entonces llamamos fetchCurrentProfile nuevamente (a través de componentHasNewIdProp ).

Cuando la nueva recuperación regresa y envía la acción redux, la propiedad currentProfile se actualiza nuevamente, el estado se actualiza y el componente se vuelve a representar con la nueva vista.

Este ejemplo ilustra cómo debe haber un comportamiento diferente cuando el componente se monta y cuando el componente recibe nuevos accesorios. También demuestra cómo se deben verificar los accesorios en busca de cambios en el último caso y cómo ciertas funciones deben llamarse solo en ciertos cambios de accesorios.

Mirando hacia atrás, esencialmente lo que quería era una función que solo se actualizara cuando cambiaba routeParams . Sin embargo, no es necesario agregar otra función porque podemos hacer todo lo que queramos con las funciones existentes, además no queremos complicar el ciclo de vida.

Sin embargo, algo que podría ayudar aquí es más documentación sobre el ciclo de vida, ejemplos de trabajo, etc.

gracias por su ayuda chicos. Por favor, avíseme si hay algo que me haya equivocado aquí. Espero que esto sea útil para cualquiera que se encuentre con este hilo en el futuro.

@ shea256 su ejemplo parece un caso razonable para no necesitar una nueva API de poder lograr lo que necesita sin un nuevo punto de vista de API.

Describí más pensamientos en: https://gist.github.com/jimfb/9ef9b46741efbb949744 , pero todavía siento que deberíamos poder optimizar la API para escribir menos código ...

Imagina que la API te permite escribir la misma funcionalidad, pero con menos repetición:

class ProfilePage extends Component {
  static propTypes = {
    fetchCurrentProfile: PropTypes.func.isRequired,
    currentProfile: PropTypes.object.isRequired
  }
  state = {currentProfile: {}}; // not material to example (babel-stage-1)

  // called both on init and update
  willReceiveProps(nextProps) {
    if (!this.props || (this.props.routeParams.id !== nextProps.routeParams.id)
      nextProps.fetchCurrentProfile(nextProps.routeParams.id)
  }

  render() {
    var profile = this.props.currentProfile;
    return (
      <div>
        <h1>{ profile.name ? profile.name : null }</h1>
      </div>
    )
  }
}

La idea que espero que se considere es reducir el área de superficie de la API para reducir el número de rutas de código y repetición. Me pregunto si la discusión se basa en la solicitud inicial de agregar algo cuando tal vez mirarlo desde una perspectiva diferente abriría nuevos caminos.

Esta es solo una opción (mi preferencia es ReceiveProps (prevProps) porque idealmente, solo habría un método relacionado con prop donde podría usar this.props), pero la parte importante es que hay una ruta de código para la inicialización del componente y cualquier prop cambia en lugar de las diferentes rutas de código que tenemos ahora con diferentes firmas de API. Cualquiera que sea la solución, la gente acepta ser mejor.

Lo que espero es lograr una reducción en el texto estándar con este problema (¡para aclarar mi necesidad / problema!) Y me gustaría hacer una lluvia de ideas un poco más amplia sobre cómo podría verse esa solución (considere eliminar las API, cambiar el métodos de ciclo de vida significativamente, acortando los nombres de los métodos ahora que ES6 está ampliamente disponible y los componentes parecen ser el camino a seguir, etc.).

@ akinnee-gl, tener una aplicación compuesta de muchos componentes diminutos es mucho mejor que unos pocos componentes pesados ​​por varias razones:

  1. Este enfoque se conecta con el "principio de responsabilidad única"
  2. Puede ver claramente qué componente hace qué
  3. Puede componer funciones y ampliar componentes fácilmente ajustando
  4. Los métodos y las propiedades no chocan
  5. Es más fácil ampliar una función porque un componente encapsula una función

Con respecto a su aviso de que leer desde una tienda en cada renderizado puede ser ineficiente, hice una idea básica que muestra una idea básica de cómo implementar un componente de middleware para el almacenamiento en caché: https://gist.github.com/me-andre/0ca8b80239cd4624e6fc

He dado otro paso a los principios / objetivos que podrían conducir a una API mejorada (con suerte basado en un método de props recibido como se sugiere en este número):

(A) simplifique la estructura del código / reduzca el texto estándar
(B) mantenga los accesorios (entradas) separados del estado (interno)
(C) permitir un método para controlar la representación de un componente
(D) simplifique la denominación de métodos basándose en el supuesto de subclasificación de componentes
(E) reduzca el número de métodos de accesorios a uno tanto para la inicialización como para los cambios
(F) el método props debe tener this.props con el último valor

Hay aclaraciones sobre algunos de ellos en la esencia.

@kmalakoff Según la esencia, no

@kmalakoff Muchos de los puntos que mencionas son temas de otros temas. Por ejemplo, (D) es respondido por https://github.com/reactjs/react-future/issues/40#issuecomment -142442124 y https://github.com/reactjs/react-future/issues/40#issuecomment - 153440651. Esos son los hilos apropiados para tener esa discusión. Si está buscando discusiones más generales / de diseño holístico / API, el medio correcto probablemente sea https://discuss.reactjs.org/

@jimfb He pensado un poco en esto después de que sugirió que moviera esto a la esencia y, en realidad, no creo que esta línea de análisis esté fuera de tema ... la razón por la que estoy interesado en este problema en torno a los accesorios recibidos es que creo que tengo un método de ciclo de vida de los accesorios recibidos me permitirá reducir el texto estándar.

Por favor, escúchame ... reducir el texto estándar es el problema que el método del ciclo de vida en este problema podría resolver por mí.

Creo que también podría ser una parte importante de la razón por la que la gente está interesada en este método de ciclo de vida porque la API actual fomenta la repetición más de lo que nos gustaría.

Dicho esto, me complacería intentar desacoplar las mejoras de la API y, por ejemplo, centrarme solo en explorar el espacio de la solución en torno a la mejora de la API relacionada con los accesorios en este problema.

@kmalakoff El tema del hilo se define por el título y la primera publicación. El título es componentDidReceiveProps Please no lots of general ways to reduce boilerplate . Este hilo trata de reducir el texto repetitivo específicamente mediante la introducción de un método de ciclo de vida muy específico. Esa discusión por sí sola ya tiene suficientes matices, sin introducir temas adicionales (como cambiar el nombre de todas las funciones) que ya se están discutiendo en otros números.

Tus puntos son valiosos y te animo a tener la discusión, solo muévela a un hilo diferente, porque queremos mantener los hilos de github enfocados en el tema en cuestión. De lo contrario, las discusiones son demasiado difíciles de rastrear / administrar. Existen hilos para la mayoría de los temas que planteó.

@jimfb ¿ componentDidReceiveProps ? La razón por la que me interesa es para reducir el texto estándar relacionado con los accesorios . No estoy seguro de cómo expresar esto con mayor claridad para que me escuchen y entiendan.

Ha preguntado por los problemas y los casos de uso, y lo he declarado como el problema para mí y he mostrado un caso de uso arriba (respondiendo a @ shea256) que parece estar dentro del tema, dentro del alcance y, desde mi perspectiva, importante para resolver este problema.

He acordado posponer la ampliación del alcance a problemas / mejoras generales de la API y lo haré. ¡Prometo! :guiño:

Para su información: como dije anteriormente, la razón por la que comencé con el experimento mental en torno a este tema fue porque los argumentos parecían demasiado enfocados (por ejemplo, ya podemos hacer todo lo que necesitamos con la API actual) en lugar de identificar las motivaciones subyacentes detrás de la asunto. Como sabe, a veces es necesario dar un paso atrás, luego enfocarse, luego ver desde un ángulo diferente, luego revisar las suposiciones, investigar las razones subyacentes detrás de la solicitud, etc. para iterar hacia la mejor solución a un problema. ¡Con suerte, he ayudado a llamar la atención sobre esto y mi participación ayudará a resolver este problema a nuestra satisfacción! (por supuesto, también espero que la API de React evolucione para ser más fácil de usar y continuará esa discusión en otros lugares ... gracias por los enlaces)

@kmalakoff , ok, para reducir el texto crea clases o componentes reutilizables a partir de los cuales

@ me-andre eso es absolutamente una opción si este problema no conduce a una mejora de la API.

Este problema se planteó para promover la discusión en torno al deseo de realizar un cambio en la API para mejorarla. Definitivamente, se debe considerar la exploración de alternativas en el código del cliente, pero si se hace un caso sólido de que se debe cambiar la API, este tipo de soluciones serían innecesarias. Para contrarrestar el argumento, también se deben presentar casos sobre lo que debería ser una API mejorada y luego evaluarlos con la API actual para evaluarlos.

Por ejemplo, si tuviera que demostrar que la API actual sin definir una superclase personalizada podría usarse de una manera para reducir el texto estándar (por ejemplo, estamos usando la API de manera incorrecta, o hay características en la API actual que se pueden usar para lograr un nivel similar de reducción de texto estándar, etc.) o probar por qué no hay ninguna mejora posible (por ejemplo, no existen soluciones generales porque hay un caso de uso importante que no podría ser compatible con nuestras opciones de mejora de API), etc., podría hacer un caso de que la API sea lo suficientemente buena como está.

Si la API requiere que las personas escriban superclases personalizadas para patrones de flujo de control básicos y comunes, refuerza el argumento de que hay razones para mejorar la API.

Curioso: ¿cuál es la razón para no activar componentWillReceiveProps antes del montaje? Si lo piensa, el componente realmente está recibiendo accesorios cuando se inicializa. Simplemente no está recibiendo nuevos accesorios.

¿Cuáles son los casos de uso en los que solo querría activar algo al recibir nuevos accesorios (y no en la recepción inicial de accesorios)?

Podría estar perdiendo algo obvio aquí, pero solo estoy tratando de reconciliar los puntos de vista de las distintas partes.

En cualquier caso, si componentWillReceiveNewProps fueran importantes en algunos casos, aún se podría simular con un componentWillReceiveProps modificado realizando una verificación de los accesorios que ingresan.

@kmalakoff si componentWillReceiveProps activara la primera vez, ¿cumpliría con sus estándares de simplificación de API?

componentWillReceiveProps no se active en el montaje no es la razón por la que la gente está pidiendo componentDidReceiveProps . La gente pide componentDidReceiveProps porque escribieron todos sus métodos para acceder a this.props . Cuando se llama a componentWillReceiveProps , se pasan los nextProps, pero this.props aún no ha cambiado, lo que significa que tenemos que pasar nextProps en todos los métodos que se llaman en respuesta a componentWillReceiveProps , en lugar de dejarlos como están. Terminamos con una tonelada de props = props || this.props y una tonelada de pasar props en cada función que llamamos.

@ shea256 buenos puntos. Tener diferentes rutas de código para la inicialización frente a los cambios es una de las causas principales de la plantilla de prop. La otra causa raíz es tener diferentes firmas para manejar accesorios como señala @ akinnee-gl.

Es por eso que estoy tratando de expandir el espacio de la solución que se está considerando (por ejemplo, también llamar a init), ya que en realidad podría haber una solución aún mejor para reducir más la plantilla de prop.

Espero que podamos llegar más lejos:

Antes de New Hook

componentWillMount() {
  this.setup(this.props.id);
}

componentWillReceiveProps(next) {
  this.setup(next.id);
}

setup(id) {
  UserActions.load(id);
}

After New Hook (revisado)

componentDidReceiveProps(prevProps) {
  UserActions.load(this.props.id);
}

o si UserActions.load no puede verificar la identificación cargada actualmente:

componentDidReceiveProps(prevProps) {
  if (!prevProps || (prevProps.id !== this.props.id))
    UserActions.load(this.props.id);
}

@kmalakoff , lo que estoy hablando es que la mejora de API está absolutamente disponible para usted en este momento: puede crear su propia fábrica de componentes y luego compartir con nosotros (junto con ejemplos de casos de uso). Esto hará que su propuesta y sus razones sean cien veces más claras. Dado que todos los puntos del ciclo de vida ya tienen devoluciones de llamada adecuadas, puede introducir cualquier método nuevo en cualquier punto del ciclo de vida / estado de componente. Incluso puede mezclar un emisor de eventos en su componente y hacer posible adjuntar varios controladores para un cambio de estado.

@ shea256 , una posible razón para que componentWillReceiveProps no se active antes del primer renderizado es que no existe el this.props en ese momento. Lo que suele hacer en componentWillReceiveProps es comparar this.props[propName] con newProps[propName] . Si se activa el método antes del primer renderizado, también tendría que comprobar la presencia de this.props . Además, todo el componente está completamente sin inicializar en el momento en que recibe props antes del renderizado inicial, ni siquiera tiene state .

@kmalakoff , he publicado dos veces ejemplos de código que muestran cómo organizar un componente de React de una manera que no necesita setup o trucos similares. ¿Podría decirnos por qué todavía se esfuerza por cambiar el comportamiento de un componente de React en lugar de ajustar su código para que se integre con React? Sería genial si solo señalara dónde mis muestras son inapropiadas para su caso de uso.

@ akinnee-gl, la razón para no introducir un nuevo método para acceder a this.props en la actualización es que tenemos dicho método, se llama render() . Incluso se llama después de un cheque por shouldComponentUpdate() , que normalmente es un lugar donde haces this.props !== newProps o _.isEqual(this.props, newProps) etc.
Si cree que debería tener un método separado para alguna configuración, ¿por qué no simplemente subclasifica un componente React y define el siguiente método render()

this.setup(this.props);
return this._render();

Simplemente no veo cómo simplifica las cosas en el ecosistema React, pero eso es lo que sigues solicitando.

@ me-andre la premisa de este problema no es que no podamos lograr lo que queremos con la API actual ni que no podamos evitar la API actual en el código del cliente. La premisa de este problema es que la API de React actual no es óptima y debe mejorarse ; por ejemplo, si tuviera que idear principios de cómo se vería una API óptima (como lo intenté anteriormente), la API React actual obtendría una puntuación en un rango de nivel medio porque es subóptima / deficiente en varias áreas.

Desafortunadamente, proporcionar formas en el código del cliente para evitar la API de React no aborda las causas raíz del problema, aleja el debate de abordar los problemas subyacentes y no conducirá a un debate sobre posibles soluciones para mejorar la API de React.

En resumen, ya tengo soluciones alternativas que me funcionan porque tengo un montón de aplicaciones React en producción, por lo que las mejores prácticas podrían ser excelentes para una publicación de blog, pero usarlas como una línea de debate en este número simplemente nos desviará de un debate real sobre el verdadero propósito de este problema: cómo mejorar la API de React (en este número, limitado a casos de uso de accesorios y texto estándar).

React 1.0 debería apuntar a la parte superior, no al medio. Un aumento importante de la versión puede romper la compatibilidad con versiones anteriores, así que busquemos la mejor API posible basada en lo que hemos aprendido de todos estos años de uso de la versión 0.x.

(Para su información: creo que la gente podría no interactuar con sus ejemplos tanto como usted espera porque no vienen aquí para aprender sobre la API actual, sino porque están buscando / esperando mejoras en la API, por ejemplo, ellos podría percibirse como con buenas intenciones, pero ligeramente desalineado)

Creo que es posible que las personas no se involucren con tus ejemplos tanto como esperas porque no vienen aquí para aprender sobre la API actual, sino porque están buscando / esperando mejoras en la API.

Ok, vienes con una propuesta de mejora de la API, pero luego muestras el código que está demasiado lejos de "Reaccionar las mejores prácticas". Algunas cosas se parecen mucho a las peores prácticas. Luego dices: "esa es la razón del cambio". Sí, ese es el motivo de un cambio, pero no en el código base de React.
Le muestro cómo reorganizar el código para que funcione bien con React, pero simplemente ignore el escaparate de uso adecuado y siga insistiendo. Ese no parece ser un debate constructivo.

Desafortunadamente, proporcionar formas en el código del cliente para solucionar la API de React no aborda las causas raíz del problema.

Cuando envuelve una API de bajo nivel con una API de nivel superior, eso no es una solución alternativa, sino una práctica común. Muchos marcos brillantes siguen esa idea.
Lo que sigo diciendo es envolver la fea API existente con lo que le gustaría usar y compartir con nosotros. Junto con los ejemplos de uso y las explicaciones de por qué mejoró, sería increíble.
Una vez más, no veo una razón por la que no hagas eso. Porque si de lo que estás hablando es un problema común, sería de gran ayuda para muchas personas.

Tengo un montón de aplicaciones React en producción, por lo que las mejores prácticas podrían ser excelentes para una publicación de blog, pero usarlas como una línea de debate en este número solo nos desviará de un debate real.

Lo que suele hacer cuando diseña / diseña una API es predecir problemas, formular hipótesis sobre casos de uso, etc. La razón por la que las personas plantean hipótesis no es que traten de abstraerse de un mundo real en busca del ideal. Es simplemente falta de experiencia: no se puede adquirir experiencia "por adelantado".

Dices que tienes esa experiencia, has visto problemas reales y tienes algunas soluciones que puedes compartir. Eso es exactamente lo que puede convertir este debate en "real" y "efectivo". El propio React se basó en problemas y desafíos reales, por eso resuelve problemas de arquitectura reales y no solo "cómo escribir hola mundo en 3 líneas".

@ me-andre te escucho y tu línea de argumentación es clara.

Tiene razón en que lo fundamental de mi argumento es que si establecemos mejores principios basados ​​en nuestras experiencias colectivas de uso de la API de React actual y abrimos un debate para incluir soluciones no dogmáticas que puedan cambiar la API de algunas formas fundamentales, podemos y debemos Aproveche la oportunidad de mejorar la API de React para que sea aún mejor de lo que es hoy. ¡No nos durmamos en los laureles cuando hay margen para mejorar!

¿Cómo calificaría la API de React actual en torno a los accesorios? (digamos que usa calificaciones con letras: F a A +), ¿por qué y qué haría para mejorarlo si es menor que A +?

@ me-andre, ¿ha tenido la oportunidad de preparar su ranking y análisis? Me interesa escuchar cuáles cree que son las fortalezas, debilidades y oportunidades de la API actual.

+1

+1 por favor

¿Existe alguna solución alternativa? Necesito ComponentDidReceiveProps

Hice este problema hace más de un año y he estado usando React diariamente desde entonces. Ya no creo que exista un caso de uso para componentDidReceiveProps que justifique aumentar la API.

@AlexCppns ¿qué es lo que está tratando de hacer?

@iammerrick , en realidad está bien, simplemente

Me encontré con esto varias veces, y lo que terminé haciendo fue:

componentWillReceiveProps(nextProps) {
  if (this.props.foo !== nextProps.foo) this.needsUpdate = true;
}
componentDidUpdate() {
  if (this.needsUpdate) {
    this.needsUpdate = false;
    // update the dom
  }
}

No es tan malo.

@brigand , no hay necesidad de una bandera; puedes comparar accesorios dentro de componentDidUpdate() :

componentDidUpdate(prevProps) {
    let needsUpdate = prevProps.foo !== this.props.foo;
    // ...whatever
}

Además, su solución podría romperse fácilmente con shouldComponentUpdate() , lo que puede evitar que se vuelva a renderizar.

¡Oh genial, gracias!

@jimfb Creo que tengo un caso de uso sincrónico a continuación. Creo que un componetDidReceiveProps hubiera sido perfecto para esto.

  componentDidMount() {
    this._selectAll()
  }

  componentDidUpdate(prevProps) {
    let shouldUpdateSelected = (prevProps.recordTypeFilter !== this.props.recordTypeFilter) ||
      (prevProps.statusFilter !== this.props.statusFilter) ||
      (prevProps.list !== this.props.list)

    if (shouldUpdateSelected) { this._selectAll() }
  }

  _selectAll() {
    this.setState({ selectedIds: this._getFilteredOrders().map((order) => ( order.id )) })
  }

  _getFilteredOrders() {
    let filteredOrders = this.props.list

    // recordTypeFilter
    let recordTypeFilter = this.props.recordTypeFilter
    filteredOrders = _.filter(filteredOrders, (order) => {
        // somelogic
    })

    // statusFilter
    let statusFilter = this.props.statusFilter
    filteredOrders = _.filter(filteredOrders, (order) => {
        // somelogic
    })

    return filteredOrders
  }

@chanakasan , su ejemplo carece del método render() , que es esencial tanto para comprender su ejemplo como para sugerir una mejor solución.
En segundo lugar, su código está conectado a una lógica empresarial personalizada y no es fácil de leer. No dude en explicarlo, no solo en copiar y pegar a los demás.
Sin embargo, he leído su ejemplo y me gustaría compartir los siguientes pensamientos:

  1. Este es el caso de uso de componentWillReceiveProps , no componentDidUpdate . Si cambia a componentWillReceiveProps , su componente volvería a renderizarse dos veces menos mientras conserva la misma lógica. Después de un año desde que dejé esta discusión, todavía veo un caso de uso cero para componentDidUpdate excepto para reaccionar a los cambios de DOM.
  2. Mucho mejor, sin embargo, sería evitar los ganchos y mover toda la lógica al método render() / migrar a un componente sin estado, porque this.state.selectedIds no es en realidad un estado. Está puramente calculado a partir de accesorios.

Hola @ me-andre, Gracias por tomarse el tiempo para responder. He leído la discusión en esta página y me gustaría agradecerles por participar en ella.

Sí, no se necesita un componentDidReceiveProps , pero las cosas parecen misteriosas sin él.
Ha dicho your component would re-render twice as less si uso componentWillReceiveProps . Eso sigue siendo un misterio para mí.

Pensé y probé las dos cosas que sugirió antes, pero fracasé.

  1. componentWillReceiveProps no funcionará porque la función _getFilteredOrders() llamada desde _selectAll() necesita newProps.
  2. No pude pensar en una forma de derivar el selectedIds sin almacenarlo en el estado.

Acabo de crear un ejemplo completo de mi caso de uso. Lo he facilitado todo lo posible.

Si pudiera, por favor, eche un vistazo y apúnteme en la dirección correcta. Si puedo entender esto correctamente, compartiré este ejemplo en una publicación de blog para ayudar a otros también.

@chanakasan , vamos, esto parece un código de producción. No voy a leerlo todo y ayudarlo con su proyecto de forma gratuita.

Sin embargo, puedo señalarle la dirección correcta:

  1. Tanto componentWillReceiveProps() como componentDidUpdate() pueden acceder a los accesorios anterior y siguiente. Eso se ve claramente en los documentos oficiales de React.
  2. A partir de la copia completa de su código, ahora es obvio que usa el estado para almacenar identificadores seleccionados que un usuario puede alternar. Está bien usar el estado entonces, pero aún así componentWillReceiveProps() activaría la re-renderización dos veces menos. (debido a que un render va a suceder después de componentWillReceiveProps() todos modos, setState() simplemente actualizaría el estado para el próximo render).
  3. Por favor, eche un vistazo a los documentos . Ahorre su tiempo y respete a los demás.

@ me-andre creo que entiendo su punto sobre componentWillReceiveProps usando esta línea en los documentos:

componentWillReceiveProps () no se invoca si simplemente llama a this.setState ()

Pero la advertencia de usar componentWillReceiveProps es que tendré que pasar nextProps a las funciones.

Intentaré seguir tu consejo. Gracias lo aprecio.

Por cierto, no era mi intención que me ayudaras con mi proyecto de producción de forma gratuita :). Es un ejemplo más completo de mi ejemplo corto anterior para completar los espacios en blanco sobre mi caso de uso.

¿Qué pasa si usamos esto junto con shouldComponentUpdate?
donde no deseamos actualizar el estado del componente o volver a procesarlo,
pero necesitamos que this.props utilice los últimos accesorios para hacer un trabajo de script manual después de recibir los accesorios

mi trabajo es configurar this.props a los nuevos accesorios antes de ejecutar la función deseada

Realmente sería bueno tener ComponentDidMount o algún gancho para eso. ¿Por qué? Porque, a veces, necesitamos hacer otros cálculos que no dependen del ciclo de vida de React.

Por ejemplo: tengo un componente principal que puede cambiar de tamaño. El componente hijo es responsable de representar un mapa OpenLayer que tiene una función que es responsable de cambiar el tamaño del mapa. Sin embargo, debería suceder después de que el niño obtenga los accesorios del padre y también haya realizado otros cálculos dentro del ciclo de vida de React.

¿Fue útil esta página
0 / 5 - 0 calificaciones