reactjs - qué - react comunicacion entre componentes
cómo renderizar componentes de reacción usando map y join (14)
¿Soy el único que piensa que hay muchas extensiones y anidamientos innecesarios en las respuestas aquí? Puntos por ser conciso, claro, pero deja la puerta abierta a problemas de escala o React que cambian la forma en que tratan con matrices / Fragmentos anidados.
const joinJsxArray = (arr, joinWith) => {
if (!arr || arr.length < 2) { return arr; }
const out = [arr[0]];
for (let i = 1; i < arr.length; i += 1) {
out.push(joinWith, arr[i]);
}
return out;
};
// render()
<div>
{joinJsxArray(this.props.data.map(t => <span>t</span>), '', '')}
</div>
Una matriz, sin anidamiento. Tampoco es un método atractivo para encadenar, pero si te encuentras haciendo esto a menudo, siempre puedes agregarlo al prototipo de matriz o envolverlo en una función que también tenga una devolución de llamada de mapeo para hacerlo todo de una vez.
Tengo un componente que mostrará Array of String. El código se ve así.
React.createClass({
render() {
<div>
this.props.data.map(t => <span>t</span>)
</div>
}
})
Está funcionando perfectamente bien. es decir, si props.data = [''tom'', ''jason'', ''chris''] El resultado representado en la página sería tomjasonchris
Luego, quiero unir todos los nombres usando una coma, así que cambio el código a
this.props.data.map(t => <span>t</span>).join('', '')
Sin embargo, el resultado representado es [Objeto], [Objeto], [Objeto].
No sé cómo interpretar el objeto para que se convierta en un componente de reacción para ser renderizado. Cualquier sugerencia ?
Esto debería devolver una matriz plana. Maneje el caso con el primer objeto no iterable al proporcionar una matriz vacía inicial y filtra la primera coma no necesaria del resultado
[{}, ''b'', ''c''].reduce((prev, curr) => [...prev, '', '', curr], []).splice(1) // => [{}, ''b'', ''c'']
Esto funcionó para mí:
{data.map( ( item, i ) => {
return (
<span key={i}>{item.propery}</span>
)
} ).reduce( ( prev, curr ) => [ prev, '', '', curr ] )}
La respuesta aceptada en realidad devuelve una matriz de matrices porque
prev
será una matriz cada vez.
React es lo suficientemente inteligente como para hacer que esto funcione, pero es propenso a causar problemas en el futuro, como romper el algoritmo diferente Reacts al dar claves para cada resultado del mapa.
La nueva característica
React.Fragment
nos permite hacer esto de una manera fácil de entender sin los problemas subyacentes.
class List extends React.Component {
render() {
<div>
{this.props.data
.map((t, index) =>
<React.Fragment key={index}>
<span> {t}</span> ,
</React.Fragment>
)
</div>
}
}
Con
React.Fragment
simplemente podemos colocar el separador
,
fuera del HTML devuelto y React no se quejará.
Mi variante:
{this.props.data
.map(item => <span>{item}</span>)
.map((item, index) => [index > 0 && '', '', item ])}
Puede usar
reduce
para combinar varios elementos de una matriz:
React.createClass({
render() {
<div>
this.props.data
.map(t => <span>t</span>)
.reduce((accu, elem) => {
return accu === null ? [elem] : [...accu, '','', elem]
}, null)
</div>
}
})
Esto inicializa el acumulador con nulo, por lo que podemos envolver el primer elemento en una matriz.
Para cada elemento siguiente en la matriz, construimos una nueva matriz que contiene todos los elementos anteriores usando el
...-operator
, agregamos el separador y luego el siguiente elemento.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
Si solo quiero renderizar una matriz de componentes separados por comas, generalmente encuentro
reduce
demasiado detallado.
Una solución más corta en tales casos es
{arr.map((item, index) => (
<Fragment key={item.id}>
{index > 0 && '', ''}
<Item {...item} />
</Fragment>
))}
{index > 0 && '', ''}
representará una coma seguida de un espacio delante de todos los elementos de la matriz, excepto el primero.
Si desea separar el penúltimo elemento y el último por otra cosa, diga la cadena
'' and ''
, puede reemplazar
{index > 0 && '', ''}
con
{index > 0 && index !== arr.length - 1 && '', ''}
{index === arr.length - 1 && '' and ''}
Una solución simple es usar
reduce()
sin un segundo argumento y sin difundir el resultado anterior:
class List extends React.Component {
render() {
<div>
{this.props.data
.map(t => <span>{t}</span>)
.reduce((prev, curr) => [prev, '', '', curr])}
</div>
}
}
Sin un segundo argumento,
reduce()
comenzará en el índice 1 en
lugar de 0, y React está perfectamente satisfecho con las matrices anidadas.
Como se dijo en los comentarios, solo desea usar esto para matrices con al menos un elemento, porque
reduce()
sin un segundo argumento arrojará una matriz vacía.
Normalmente, esto no debería ser un problema, ya que de todos modos desea mostrar un mensaje personalizado que diga algo como ''esto está vacío'' para las matrices vacías.
Actualización para mecanografiado
Puede usar esto en Typecript (sin type-inseguro) con un parámetro de tipo
React.ReactNode
en
.map()
:
class List extends React.Component {
render() {
<div>
{this.props.data
.map<React.ReactNode>(t => <span>{t}</span>)
.reduce((prev, curr) => [prev, '', '', curr])}
</div>
}
}
Usando es6 podrías hacer algo como:
const joinComponents = (accumulator, current) => [
...accumulator,
accumulator.length ? '', '' : '''',
current
]
Y luego corre:
listComponents
.map(item => <span key={item.id}> {item.t} </span>)
.reduce(joinComponents, [])
Use una matriz anidada para mantener "," afuera.
<div>
{this.props.data.map((element, index) => index == this.props.data.length - 1 ? <span key={index}>{element}</span> : [<span key={index}>{element}</span>, ", "])}
</div>
Optimízalo guardando los datos en una matriz y modifica el último elemento en lugar de verificar si es su último elemento todo el tiempo.
let processedData = this.props.data.map((element, index) => [<span key={index}>{element}</span>, ", "])
processedData [this.props.data.length - 1].pop()
<div>
{processedData}
</div>
el
<span>{t}</span>
que está devolviendo es un objeto, no una cadena.
Verifique los documentos de reacción al respecto
https://facebook.github.io/react/docs/jsx-in-depth.html#the-transform
Al usar
.join()
en la matriz devuelta del
map
, se está uniendo a la matriz de objetos.
[object Object], ...
Puede poner la coma dentro de
<span></span>
para que se represente de la manera que desee.
render() {
return (
<div>
{ this.props.data.map(
(t,i) => <span>{t}{ this.props.data.length - 1 === i ? '''' : '',''} </span>
)
}
</div>
)
}
Como mencionó Pith , React 16 le permite usar cadenas directamente, por lo que ya no es necesario envolver las cadenas en etiquetas de extensión. Sobre la base de la respuesta de Maarten , si también desea tratar un mensaje personalizado de inmediato (y evitar arrojar un error en una matriz vacía), puede dirigir la operación con una declaración if ternaria en la propiedad de longitud en la matriz. Eso podría verse más o menos así:
class List extends React.Component {
const { data } = this.props;
render() {
<div>
{data.length
? data.reduce((prev, curr) => [prev, '', '', curr])
: ''No data in the array''
}
</div>
}
}
Actualice con React 16:
ahora está permitido procesar cadenas directamente, por lo que puede simplificar su código eliminando todas las etiquetas
<span>
inútiles.
const List = ({data}) => data.reduce((prev, curr) => [prev, '', '', curr])
function YourComponent(props) {
const criteria = [];
if (something) {
criteria.push(<strong>{ something }</strong>);
}
// join the jsx elements with `, `
const elements = criteria.reduce((accu, elem) => {
return accu === null ? [elem] : [...accu, '', '', elem]
}, null);
// render in a jsx friendly way
return elements.map((el, index) => <React.Fragment key={ index }>{ el }</React.Fragment> );
}