Estoy construyendo un elemento con un número desconocido de niños usando React y necesito una referencia para cada uno. Considere el siguiente código:
class Items extends React.Component { constructor() { super(); this.itemEls = []; } render() { return ( <div> { this.props.items.map((item, index) => { return <div className="item" key={index} ref={(el) => this.itemEls.push(el)}>{item}</div>; }) } </div> ); } componentDidMount() { console.log(this.itemEls.map((el) => el.innerHTML)); } } ReactDOM.render(<Items items={["Hello", "World", "Foo", "Bar"]} />, document.getElementById('container')); .item { display: inline-block; background: #EEE; border: 1px solid #DDD; color: #999; padding: 10px; font-family: sans-serif; font-weight: bold; margin: 10px; border-radius: 5px; cursor: default; } <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script> <div id="container"></div>Recorro la matriz proporcionada y guardo una referencia a cada elemento a medida que avanzo. Cuando se monta el componente, imprimo una lista de los contenidos de los elementos en el orden en que se agregaron a la matriz. Hasta ahora, en todas mis pruebas, el orden ha sido correcto, pero no puedo encontrar ninguna documentación que confirme que este no es solo el resultado más común de una condición de carrera secreta que ocurre en segundo plano.
¿Es consistente el orden en que se llaman las funciones de referencia?
La solución obvia aquí sería preestablecer la longitud de la matriz en componentWillMount y llenarla con el valor del index de this.props.items.map , pero por el bien del argumento, digamos que eso no es posible en la situación real en la que me encuentro. en.
La documentación dice:
El atributo ref toma una función de devolución de llamada, y la devolución de llamada se ejecutará inmediatamente después de montar o desmontar el componente.
Los componentes montados usando el map obviamente están montados en el orden correcto, así que supongo que las devoluciones de llamada de referencia se ejecutan en orden. Si sospecha que las devoluciones de llamada de referencia pueden ser asíncronas, creo que puede verificar esta suposición arrojando un error desde la devolución de llamada: si la ejecución se detiene en el primer elemento, entonces es sincrónica (por favor, que alguien me corrija si me equivoco).
Si todavía está preocupado, puede agregar los elementos a la matriz por el índice del elemento en lugar de empujar (que es probablemente lo que haría de todos modos):
this.props.items.map((item, index) => { return <div className="item" key={index} ref={(el) => {this.itemEls[index] = el} }>{item}</div>; }) De esta manera, estará protegido contra los elementos vacíos que se saltan en el map , por ejemplo:
let a = []; let b = []; let items = ['Hello', 'World', 'Foo', , ,'Bar']; // items.length == 6 delete items[1]; items.map( i => { a.push( i ); return i }); console.log( a ); // [ "Hello", "Foo", "Bar" ] // - the index of "Foo" and "Bar" doesn't match original index items.map( (i, idx) => { b[idx] = i; return i }); console.log( b ); // [ "Hello", <empty>, "Foo", <empty>, <empty>, "Bar" ] // sparse array, but matching indexes