javascript - loop - react map object
Bucle en el interior React JSX (30)
ES2015 Array.from con la función de mapa + tecla
Si no tiene nada en .map()
, puede usar Array.from()
con mapFn
para repetir elementos:
<tbody>
{Array.from({ length: 5 }, (v, k) => <ObjectRow key={k} />)}
</tbody>
Estoy tratando de hacer algo como lo siguiente en React JSX (donde ObjectRow es un componente separado):
<tbody>
for (var i=0; i < numrows; i++) {
<ObjectRow/>
}
</tbody>
Me doy cuenta y entiendo por qué esto no es JSX válido, ya que JSX se asigna a llamadas de función. Sin embargo, al provenir de la plantilla y ser nuevo en JSX, no estoy seguro de cómo lograría lo anterior (agregar un componente varias veces).
... O también puede preparar una matriz de objetos y asignarlos a una función para obtener el resultado deseado. Prefiero esto, porque me ayuda a mantener la buena práctica de codificación sin lógica dentro del retorno de render.
render() {
const mapItem = [];
for(let i =0;i<item.length;i++)
mapItem.push(i);
const singleItem => (item, index) {
// item the single item in the array
// the index of the item in the array
// can implement any logic here
return (
<ObjectRow/>
)
}
return(
<tbody>{mapItem.map(singleItem)}</tbody>
)
}
Aquí hay un ejemplo de React doc: https://facebook.github.io/react/docs/jsx-in-depth.html#javascript-expressions-as-children
function Item(props) {
return <li>{props.message}</li>;
}
function TodoList() {
const todos = [''finish doc'', ''submit pr'', ''nag dan to review''];
return (
<ul>
{todos.map((message) => <Item key={message} message={message} />)}
</ul>
);
}
como tu caso, te sugiero escribir así:
function render() {
return (
<tbody>
{numrows.map((roe, index) => <ObjectRow key={index} />)}
</tbody>
);
}
Tenga en cuenta que la clave es muy importante, ya que React usa la clave para diferir los datos en la matriz.
Aquí hay una solución simple para ello.
var Object_rows=[];
for (var i=0; i < numrows; i++) {
Object_rows.push(<ObjectRow/>)
}
<tbody>
{Object_rows}
</tbody>
No se requiere asignación y código complejo. Solo necesita empujar las filas a la matriz y devolver los valores para representarlo.
Digamos que tenemos una gran variedad de artículos en su estado:
[{name: "item1", id: 1}, {name: "item2", id: 2}, {name: "item3", id: 3}]
<tbody>
{this.state.items.map((item) => {
<ObjectRow key={item.id} name={item.name} />
})}
</tbody>
Esto se puede hacer de múltiples maneras.
- Como se sugirió anteriormente, antes de
return
almacene todos los elementos en la matriz Lazo dentro de
return
Método 1
let container =[]; let arr = [1,2,3] //can be anything array, object arr.forEach((val,index)=>{ container.push(<div key={index}> val </div>) /** * 1. All loop generated elements require a key * 2. only one parent element can be placed in Array * e.g. container.push(<div key={index}> val </div> <div> this will throw error </div> ) **/ }); return ( <div> <div>any things goes here</div> <div>{container}</div> </div> )
Método 2
return( <div> <div>any things goes here</div> <div> {(()=>{ let container =[]; let arr = [1,2,3] //can be anything array, object arr.forEach((val,index)=>{ container.push(<div key={index}> val </div>) }); return container; })()} </div> </div> )
Gran pregunta
Lo que hago cuando quiero agregar un cierto número de componentes es usar una función auxiliar.
Define una función que devuelve JSX:
const myExample = () => {
let myArray = []
for(let i = 0; i<5;i++) {
myArray.push(<MyComponent/>)
}
return myArray
}
//... in JSX
<tbody>
{myExample()}
</tbody>
Hay varias maneras de hacer esto. JSX finalmente se compila en JavaScript, por lo tanto, mientras escribas JavaScript válido, serás bueno.
Mi respuesta apunta a consolidar todas las formas maravillosas ya presentadas aquí:
Si no tiene una matriz de objetos, simplemente el número de filas:
dentro del bloque de return
, creando un Array
y usando Array.prototype.map
:
render() {
return (
<tbody>
{Array(numrows).fill(null).map((value, index) => (
<ObjectRow key={index}>
))}
</tbody>
);
}
fuera del bloque de return
, simplemente use un JavaScript for-loop normal:
render() {
let rows = [];
for (let i = 0; i < numrows; i++) {
rows.push(<ObjectRow key={i}/>);
}
return (
<tbody>{rows}</tbody>
);
}
Expresión de la función invocada de inmediato:
render() {
return (
<tbody>
{() => {
let rows = [];
for (let i = 0; i < numrows; i++) {
rows.push(<ObjectRow key={i}/>);
}
return rows;
}}
</tbody>
);
}
Si tienes una matriz de objetos
dentro del bloque de return
, .map()
cada objeto a un componente <ObjectRow>
:
render() {
return (
<tbody>
{objectRows.map((row, index) => (
<ObjectRow key={index} data={row} />
))}
</tbody>
);
}
fuera del bloque de return
, simplemente use un JavaScript for-loop normal:
render() {
let rows = [];
for (let i = 0; i < objectRows.length; i++) {
rows.push(<ObjectRow key={i} data={objectRows[i]} />);
}
return (
<tbody>{rows}</tbody>
);
}
Expresión de la función invocada de inmediato:
render() {
return (
<tbody>
{() => {
let rows = [];
for (let i = 0; i < objectRows.length; i++) {
rows.push(<ObjectRow key={i} data={objectRows[i]} />);
}
return rows;
}}
</tbody>
);
}
Hay varias respuestas que apuntan a usar la declaración del map
. Este es un ejemplo completo que utiliza un iterador dentro del componente FeatureList para enumerar los componentes de la Característica en función de una estructura de datos JSON denominada características .
const FeatureList = ({ features, onClickFeature, onClickLikes }) => (
<div className="feature-list">
{features.map(feature =>
<Feature
key={feature.id}
{...feature}
onClickFeature={() => onClickFeature(feature.id)}
onClickLikes={() => onClickLikes(feature.id)}
/>
)}
</div>
);
Puede ver el código completo de FeatureList en GitHub. El accesorio de características se enumera aquí .
Lo uso como
<tbody>
{ numrows ? (
numrows.map(obj => { return <ObjectRow /> })
) : null}
</tbody>
No estoy seguro de si esto funcionará para su situación, pero a menudo el map es una buena respuesta.
Si este fue tu código con el bucle for:
<tbody>
for (var i=0; i < objects.length; i++) {
<ObjectRow obj={objects[i]} key={i}>
}
</tbody>
Podrías escribirlo así con el map :
<tbody>
{objects.map(function(object, i){
return <ObjectRow obj={object} key={i} />;
})}
</tbody>
Sintaxis de ES6:
<tbody>
{objects.map((object, i) => <ObjectRow obj={object} key={i} />)}
</tbody>
Piensa en ello como si estuvieras llamando a funciones de JavaScript. No puedes poner un bucle for
dentro de una llamada de función:
return tbody(
for (var i = 0; i < numrows; i++) {
ObjectRow()
}
)
Pero puedes hacer una matriz y luego pasarla a:
var rows = [];
for (var i = 0; i < numrows; i++) {
rows.push(ObjectRow());
}
return tbody(rows);
Puede usar básicamente la misma estructura cuando trabaje con JSX:
var rows = [];
for (var i = 0; i < numrows; i++) {
// note: we add a key prop here to allow react to uniquely identify each
// element in this array. see: https://reactjs.org/docs/lists-and-keys.html
rows.push(<ObjectRow key={i} />);
}
return <tbody>{rows}</tbody>;
Por cierto, mi ejemplo de JavaScript es casi exactamente en lo que se transforma ese ejemplo de JSX. Juega un rato con Babel REPL para tener una idea de cómo funciona JSX.
Por supuesto, puede resolver con un .map como lo sugiere la otra respuesta. Si ya usas babel, podrías pensar en usar jsx-control-statements Necesitan un poco de configuración, pero creo que vale la pena en términos de legibilidad (especialmente para los desarrolladores que no reaccionan). Si usa un linter, también hay eslint-plugin-jsx-control-statements
Puedes hacer algo como:
let foo = [1,undefined,3]
{ foo.map(e => !!e ? <Object /> : null )}
Sé que este es un hilo antiguo, pero es posible que desee revisar http://wix.github.io/react-templates/ , que le permite usar plantillas de estilo jsx en reaccionar, con algunas directivas (como rt- repetir).
Su ejemplo, si usara react-templates, sería:
<tbody>
<ObjectRow rt-repeat="obj in objects"/>
</tbody>
Si aún no tiene una matriz para map()
como la respuesta de @ FakeRainBrigand, y desea alinear esto para que el diseño de origen se corresponda con la salida más cercana a la respuesta de @ SophieAlpert:
Con la sintaxis ES2015 (ES6) (funciones de dispersión y flecha)
http://plnkr.co/edit/mfqFWODVy8dKQQOkIEGV?p=preview
<tbody>
{[...Array(10)].map((x, i) =>
<ObjectRow key={i} />
)}
</tbody>
Re: transpillando con Babel, su página de advertencias dice que Array.from
es necesario para la propagación, pero en la actualidad ( v5.8.23
) eso no parece ser el caso cuando se difunde un Array
real. Tengo un problema de documentación abierto para aclarar eso. Pero úsalo bajo tu propio riesgo o polyfill.
Vainilla es5
Array.apply
<tbody>
{Array.apply(0, Array(10)).map(function (x, i) {
return <ObjectRow key={i} />;
})}
</tbody>
Inline IIFE
http://plnkr.co/edit/4kQjdTzd4w69g8Suu2hT?p=preview
<tbody>
{(function (rows, i, len) {
while (++i <= len) {
rows.push(<ObjectRow key={i} />)
}
return rows;
})([], 0, 10)}
</tbody>
Combinación de técnicas de otras respuestas.
Mantenga el diseño de la fuente correspondiente a la salida, pero haga que la parte en línea sea más compacta:
render: function () {
var rows = [], i = 0, len = 10;
while (++i <= len) rows.push(i);
return (
<tbody>
{rows.map(function (i) {
return <ObjectRow key={i} index={i} />;
})}
</tbody>
);
}
Con métodos de sintaxis y Array
ES2015
Con Array.prototype.fill
, podría hacer esto como una alternativa al uso de la propagación como se ilustra arriba:
<tbody>
{Array(10).fill(1).map((el, i) =>
<ObjectRow key={i} />
)}
</tbody>
(Creo que en realidad podría omitir cualquier argumento para fill()
, pero no estoy al 100% en eso). Gracias a @FakeRainBrigand por corregir mi error en una versión anterior de la solución fill()
(ver revisiones).
key
En todos los casos, la key
attr alivia una advertencia con la versión de desarrollo, pero no es accesible en el niño. Puede pasar un atributo adicional si desea que el índice esté disponible en el elemento secundario. Ver Listas y claves para discusión.
Si opta por convertir este retorno interno () del método de render , la opción más fácil sería usar el método map () . Asigne su matriz a la sintaxis JSX usando la función map (), como se muestra a continuación ( se usa la sintaxis ES6 ).
Componente principal interno :
<tbody> { objectArray.map((object, index) => <ObjectRow key={index} object={object}>) } </tbody>
Tenga en cuenta el atributo key
agregado a su componente hijo. Si no proporcionó el atributo clave, puede ver la siguiente advertencia en su consola.
Advertencia: Cada niño en una matriz o iterador debe tener un prop "clave" único.
Ahora en el componente ObjectRow puede acceder al objeto desde sus propiedades.
Componente ObjectRow interior
const { object } = this.props
o
const object = this.props.object
Esto debería recuperar el objeto que pasó del componente principal al object
variable en el componente ObjectRow . Ahora puedes escupir los valores en ese objeto de acuerdo a tu propósito.
Referencias:
Si ya está utilizando lodash, la función _.times
es útil.
import React, { Component } from ''react'';
import Select from ''./Select'';
import _ from ''lodash'';
export default class App extends Component {
render() {
return (
<div className="container">
<ol>
{_.times(3, i =>
<li key={i}>
<Select onSelect={this.onSelect}>
<option value="1">bacon</option>
<option value="2">cheez</option>
</Select>
</li>
)}
</ol>
</div>
);
}
}
Simplemente use .map()
para recorrer su colección y devolver los elementos <ObjectRow>
con props de cada iteración.
Suponiendo que los objects
son una matriz en algún lugar ...
<tbody>
{ objects.map((obj, index) => <ObjectRow obj={ obj } key={ index }/> ) }
</tbody>
Simplemente utilizando el método Map Array con la sintaxis ES6:
<tbody>
{items.map(item => <ObjectRow key={item.id} name={item.name} />)}
</tbody>
No te olvides de la propiedad key
.
Su código JSX se compilará en código JavaScript puro, cualquier etiqueta será reemplazada por objetos ReactElement
. En JavaScript, no puede llamar a una función varias veces para recopilar sus variables devueltas.
Es ilegal, la única forma es usar una matriz para almacenar las variables de función devueltas.
O puede usar map que está disponible desde JavaScript ES5 para manejar esta situación.
Tal vez podamos escribir otro compilador para recrear una nueva sintaxis JSX para implementar una función de repetición al igual que la ng-repeat
de Angular .
También puede extraer fuera del bloque de retorno:
render: function() {
var rows = [];
for (var i = 0; i < numrows; i++) {
rows.push(<ObjectRow key={i}/>);
}
return (<tbody>{rows}</tbody>);
}
También puedes usar una función de auto-invocación:
return <tbody>
{(() => {
let row = []
for (var i = 0; i < numrows; i++) {
row.push(<ObjectRow key={i} />)
}
return row
})()}
</tbody>
Tiendo a favorecer un enfoque donde la lógica de programación sucede fuera del valor de retorno de render
. Esto ayuda a mantener lo que realmente se hace fácil de asimilar.
Así que probablemente haría algo como:
import _ from ''lodash'';
...
const TableBody = ({ objects }) => {
const objectRows = objects.map(obj => <ObjectRow object={obj} />);
return <tbody>{objectRows}</tbody>;
}
Es cierto que esta es una cantidad tan pequeña de código que, en línea, podría funcionar bien.
Una posibilidad de ES2015 / Babel es usar una función de generador para crear una matriz de JSX:
function* jsxLoop(times, callback)
{
for(var i = 0; i < times; ++i)
yield callback(i);
}
...
<tbody>
{[...jsxLoop(numrows, i =>
<ObjectRow key={i}/>
)]}
</tbody>
Usar la función de mapa de Array es una forma muy común de recorrer un Array de elementos y crear componentes de acuerdo con ellos en React , esta es una gran manera de hacer un bucle que es una forma bastante eficiente y ordenada de hacer sus bucles en JSX , no es la única Manera de hacerlo, pero la forma preferida.
Además, no olvide tener una clave única para cada iteración según sea necesario. La función de mapa crea un índice único a partir de 0, pero no se recomienda utilizar el índice, pero si su valor es único o si hay una clave única, puede usarlos:
<tbody>
{numrows.map(x=> <ObjectRow key={x.id} />)}
</tbody>
También algunas líneas de MDN si no está familiarizado con la función de mapa en Array:
map llama a una función de devolución de llamada proporcionada una vez para cada elemento en una matriz, en orden, y construye una nueva matriz a partir de los resultados. la devolución de llamada se invoca solo para los índices de la matriz que tienen valores asignados, incluidos los no definidos. No se llama para los elementos faltantes de la matriz (es decir, índices que nunca se han establecido, que se han eliminado o que nunca se han asignado un valor).
La devolución de llamada se invoca con tres argumentos: el valor del elemento, el índice del elemento y el objeto de la matriz que se está recorriendo.
Si se proporciona un parámetro thisArg para mapear, se usará como valor de devolución de llamada. De lo contrario, el valor indefinido se utilizará como este valor. Este valor finalmente observable por la devolución de llamada se determina de acuerdo con las reglas usuales para determinar esto visto por una función.
map no muta la matriz en la que se llama (aunque la devolución de llamada, si se invoca, puede hacerlo).
Ya que está escribiendo la sintaxis de Javascript dentro del código JSX, necesita envolver su Javascript en llaves.
row = () => {
var rows = [];
for (let i = 0; i<numrows; i++) {
rows.push(<ObjectRow/>);
}
return rows;
}
<tbody>
{this.row()}
</tbody>
Yo uso esto:
gridItems = this.state.applications.map(app =>
<ApplicationItem key={app.Id} app={app } />
);
PD: nunca olvides la llave o tendrás muchas advertencias!
if numrows es una matriz, y es muy simple.
<tbody>
{numrows.map(item => <ObjectRow />)}
</tbody>
El tipo de datos de matriz en React es muy mejor, la matriz puede hacer una copia de la nueva matriz y es compatible con el filtro, reducir, etc.