Cómo funcionan las teclas React y cosas divertidas que puedes hacer con ellas

React usa el keyatributo durante su fase de reconciliación para decidir qué elementos se pueden reutilizar para el siguiente render. Son importantes para listas dinámicas. React comparará las claves del nuevo elemento con las claves anteriores y 1) montará componentes que tengan una nueva clave 2) desmontará componentes cuyas claves ya no se utilicen.

Muchos desarrolladores de React han escuchado el consejo general de que no debes usar indexcomo clave. Pero, ¿qué puede salir mal exactamente cuando se usa keys de forma incorrecta? ¿Qué más podemos hacer cuando jugamos con las teclas?

Para una mejor comprensión, consideremos el ejemplo de renderizado de una lista de inputs. Al hacer clic en un botón, insertaremos un nuevo elemento con texto Frontal principio de la lista.

import React from "react";import { render } from "react-dom";class Item extends React.PureComponent { state = { text: this.props.text }; onChange = event => { this.setState({ text: event.target.value }); }; componentDidMount() { console.log("Mounted ", this.props.text); } componentWillUnmount() { console.log("Unmounting ", this.props.text); } render() { console.log("rerendering ", this.props.text); const { text } = this.state; return ( 
  • ); }}class App extends React.Component { state = { items: [ { text: "First", id: 1 }, { text: "Second", id: 2 } ] }; addItem = () => { const items = [{ text: "Front", id: Date.now() }, ...this.state.items]; this.setState({ items }); }; render() { return (
      {this.state.items.map((item, index) => ( ))}
    Add Item ); }}render(, document.getElementById("root"));

    Si lo usa indexcomo clave, sucede lo siguiente:

    CódigoSandbox

    CodeSandbox es un editor en línea diseñado para aplicaciones web. codesandbox.io

    ¿Qué pasa si se inserta otro Itemcon texto en Secondlugar de al final de la lista? Esto es lo que sucede:Front

    1. Item is an uncontrolled component: El texto que el usuario escribe en su inputcampo se almacena comostate
    2. Se { text: "Front" }inserta un nuevo elemento de datos al principio de los datos de la lista.
    3. La lista se vuelve a representar con el valor del índice como key. Por lo tanto, los componentes anteriores se reutilizan para los dos primeros elementos de datos y reciben los accesorios correctos Fronty First, pero el estado no se actualiza en Item. Es por eso que las dos primeras instancias de componentes mantienen el mismo texto.
    4. Se crea una nueva instancia de componente key: 2porque no se encontró una clave coincidente anterior. Se llena con el propsúltimo elemento de datos de la lista que es Second.

    Otro punto interesante son las renderllamadas que suceden. El elemento es a PureComponent, por lo que solo se actualiza cuando textcambia la propiedad (o estado):

    rerendering Frontrerendering Firstrerendering SecondMounted Second

    Todos los componentes se vuelven a renderizar. Esto sucede porque el elemento con key: 0se reutiliza para el primer elemento de datos y lo recibe props, pero el primer elemento de datos ahora es el nuevo Frontobjeto, lo que activa un render. Lo mismo ocurre con los otros componentes, porque los elementos de datos antiguos ahora están todos desplazados en un lugar.

    Entonces, ¿cuál es la solución? La solución es fácil: le damos a cada elemento de datos de la lista una única idvez al crearlo (¡no en cada render!) Todas las instancias de componentes se compararán con su elemento de datos correspondiente. Reciben lo mismo propsque antes, y esto evita otro render.

    Por ahora, ignoremos los beneficios de rendimiento que se obtienen al usar ids en listas dinámicas. El ejemplo muestra que los errores introducidos por claves solo ocurren con respecto a componentes no controlados , componentes que mantienen el estado interno .

    Si reescribimos Itemcomo un componente controlado, sacando el estado de él, el error desaparece.

    ¿Por qué? Nuevamente, porque el error estaba reutilizando un componente para un elemento de datos diferente. Por lo tanto, el estado interno aún reflejaba el estado del elemento de datos anterior , pero los apoyos de uno diferente . Al hacer que el componente esté controlado, eliminando su estado por completo, ya no tenemos esta discrepancia. (Pero todavía existe el problema con las renovaciones innecesarias).

    Abusar de claves para reparar componentes rotos de terceros

    React solo necesita keys cuando se hacen coincidir varios elementos, por lo que no es necesario establecer una clave en un solo niño. Pero aún puede ser útil establecer una clave en un solo componente secundario.

    Si cambia la clave, React desechará todo el componente (lo desmontará) y montará una nueva instancia de componente en su lugar. ¿Por qué podría ser útil esto?

    Una vez más, volvemos a los componentes no controlados . A veces, está utilizando un componente de terceros y no puede modificar su código para controlarlo. Si un componente tiene algún estado interno y está implementado de manera incorrecta (por ejemplo, el estado se deriva solo una vez en el constructor, pero getDerivedStateFromProps/ componentWillReceivePropsno está implementado para reflejar propscambios recurrentes en su estado interno) , la caja de herramientas estándar de React no puede ayudarlo aquí. No hay forceRemount.

    Sin embargo, podemos establecer un nuevo keycomponente en este componente para lograr el comportamiento deseado de inicializar completamente un nuevo componente. Se desmontará el componente antiguo y se montará uno nuevo con el nuevo propsinicializando el state.

    TL; DR:

    El uso indexcomo clave puede:

    1. conducir a repeticiones innecesarias
    2. introducir errores cuando los elementos de la lista son componentes no controlados pero todavía se utilizanprops

    La keypropiedad se puede utilizar para forzar el montaje completo de un componente, lo que a veces puede resultar útil.

    Publicado originalmente en cmichel.io