He estado jugando con React 16.6.0 recientemente y me encanta la idea de React Memo , pero no he podido encontrar nada con respecto a los escenarios más adecuados para implementarlo.
Los documentos de React ( https://reactjs.org/docs/react-api.html#reactmemo ) no parecen sugerir ninguna implicación por simplemente incluirlo en todos sus componentes funcionales.
Debido a que hace una comparación superficial para averiguar si necesita volver a renderizarse, ¿alguna vez habrá una situación que afecte negativamente el rendimiento?
Una situación como esta parece una opción obvia para la implementación:
// NameComponent.js import React from "react"; const NameComponent = ({ name }) => <div>{name}</div>; export default React.memo(NameComponent); // CountComponent.js import React from "react"; const CountComponent = ({ count }) => <div>{count}</div>; export default CountComponent; // App.js import React from "react"; import NameComponent from "./NameComponent"; import CountComponent from "./CountComponent"; class App extends Component { state = { name: "Keith", count: 0 }; handleClick = e => { this.setState({ count: this.state.count + 1 }); }; render() { return ( <div> <NameComponent name={this.state.name} /> <CountComponent count={this.state.count} /> <button onClick={this.handleClick}>Add Count</button> </div> ); } } Debido a que el name nunca cambiará en este contexto, tiene sentido memorizar .
Pero, ¿qué pasa con una situación en la que los accesorios cambian con frecuencia?
¿Qué sucede si agrego otro botón que cambió algo más en el estado y activó una nueva representación? ¿Tendría sentido envolver CountComponent en memo , aunque este componente por diseño está destinado a actualizarse con frecuencia?
Supongo que mi pregunta principal es , mientras todo permanezca puro, ¿hay alguna situación en la que no envuelva un componente funcional con React Memo?
React.memo LITERALMENTE, ya que comparar el árbol devuelto por el Componente siempre es más costoso que comparar un par de propiedades de props . Así que no escuches a nadie y envuelve TODOS los componentes funcionales en React.memo . Originalmente, React.memo estaba destinado a integrarse en el núcleo de los componentes funcionales, pero no se usa de forma predeterminada debido a la pérdida de compatibilidad con versiones anteriores. (Dado que compara el objeto superficialmente, y QUIZÁS esté utilizando las propiedades anidadas del subobjeto en el componente) =)
Eso es todo, esta es la ÚNICA RAZÓN por la que React no usa memo automáticamente. =)
De hecho, podrían hacer la versión 17.0.0, que ROMPERÍA la compatibilidad con versiones anteriores, y hacer que React.memo sea el predeterminado, y hacer algún tipo de función para cancelar este comportamiento, por ejemplo, React.deepProps =)
Dejen de escuchar a los teóricos, muchachos =) La regla es simple:
Si su componente usa PROPIOS DE COMPARACIÓN PROFUNDA, entonces no use memo, de lo contrario úselo SIEMPRE, comparar DOS OBJETOS SIEMPRE es MÁS BARATO que llamar a React.createElement() y comparar dos árboles, crear FiberNodes, y así sucesivamente.
Los teóricos hablan de lo que ellos mismos no saben, no han analizado el código de reacción, no entienden FRP y no entienden lo que aconsejan =)
PD: si su componente está usando accesorios para children , React.memo no funcionará, porque los accesorios para children siempre crean una nueva matriz. Pero es mejor no preocuparse por esto, e incluso tales componentes TAMBIÉN deben estar envueltos en React.memo , ya que los recursos informáticos son insignificantes.
Todos los componentes de reacción implementan el método shouldComponentUpdate() . De forma predeterminada (componentes que extienden React.Component ), esto devuelve verdadero, siempre. El cambio que introduce memorizar un componente (a través de React.memo para componentes funcionales o extender React.PureComponent para componentes de clase) es una implementación del método shouldComponentUpdate() , que hace una comparación superficial del estado y las propiedades.
Mirando la documentación sobre los métodos del ciclo de vida del componente, siempre se llama shouldComponentUpdate() antes de que ocurra el renderizado, esto significa que memorizar un componente incluirá esta comparación superficial adicional en cada actualización.
Teniendo esto en cuenta, memorizar un componente tiene efectos en el rendimiento, y la magnitud de estos efectos debe determinarse mediante el perfilado de su aplicación y determinando si funciona mejor con o sin memorización.
Para responder a su pregunta, no creo que haya una regla explícita sobre cuándo debe o no memorizar componentes, sin embargo, creo que se debe aplicar el mismo principio que cuando decide si debe anular o no shouldComponentUpdate() : encuentre problemas de rendimiento a través del herramientas de generación de perfiles sugeridas e identificar si necesita o no optimizar un componente.
¿Habrá alguna vez una situación que afecte negativamente el rendimiento?
Sí. Puede terminar con un peor rendimiento, si React.memo envuelve todos los componentes sin pensar.
No es necesario en muchos casos. Para probarlo con un componente crítico para el rendimiento, realice primero algunas medidas, agregue memorización y luego vuelva a medir para ver si la complejidad adicional valió la pena.
React.memo ? Un componente memorizado compara los accesorios antiguos con los nuevos para decidir, si volver a renderizar, cada ciclo de renderizado .
A un componente simple no le importa y solo se renderiza, después de los accesorios/cambio de estado en un padre.
Eche un vistazo a la implementación de shallowEqual , que se invoca en updateMemoComponent .
React memo ? No hay reglas estrictas. Cosas que afectan negativamente a React.memo :
Anuncio 1: en este caso, React.memo no puede evitar que se vuelva a procesar, pero tuvo que hacer cálculos adicionales.
Anuncio 2: el costo de comparación agregado no vale la pena para un componente "simple" en términos de procesamiento, reconciliación, cambio de DOM y costos de efectos secundarios.
Anuncio 3: Cuantos más apoyos, más cálculos. También puede pasar un comparador personalizado más complejo.
React.memo ? Solo verifica accesorios, no cambios de contexto o cambios de estado desde adentro. React.memo también es inútil si el componente memorizado tiene children no primitivos. useMemo puede complementar memo aquí, como:
// inside React.memo component const ctxVal = useContext(MyContext); // context change normally trigger re-render return useMemo(() => <Child />, [customDep]) // prevent re-render of children