Artigo original: Iterate Through Nested Object in React.js
Se você já trabalhou com APIs, sabe que a estrutura dos dados que elas retornam pode ficar complicada rapidamente.
Imagine chamar uma API a partir do seu projeto em React e a resposta ter uma aparência semelhante a esta:
Objeto1 {
Objeto2 {
propriedadeQueEuQueroAcessar:
outraPropriedadeQueEuQueroAcessar:
}
}
Você armazenou os dados no estado (em inglês, state) do seu componente como this.state.myPosts
, podendo acessar os elementos de do objeto externo da seguinte maneira:
render() {
console.log(this.state.myPosts);
const data = this.state.myPosts;
const display = Object.keys(data).map((d, key) => {
return (
<div className="my-posts">
<li key={key}>
{data.current_route}
</li>
</div>
);
});
return(
<div>
<ul>
{ display }
</ul>
</div>
);
}
O problema, contudo, é o fato de que você não consegue acessar nada nos objetos internos.
Os valores dos objetos internos sempre mudarão. Assim, você não consegue simplesmente inserir os valores dessas chaves no código diretamente e percorrer essas chaves para obter os valores adequados.
Soluções possíveis
Pode ser difícil trabalhar diretamente com respostas complexas de APIs. Assim, vamos voltar um pouco e simplificar:
const visit = (obj, fn) => {
const values = Object.values(obj)
values.forEach(val =>
val && typeof val === "object" ? visit(val, fn) : fn(val))
}
// Teste rápido
const print = (val) => console.log(val)
const person = {
name: {
first: "João",
last: "Ninguém"
},
age: 15,
secret: {
secret2: {
secret3: {
val: "Comi um cookie"
}
}
}
}
visit(person, print)
/* Resultado
João
Ninguém
15
Comi um cookie
*/
A biblioteca lodash
tem métodos simples de se conseguir a mesma coisa, mas essa é uma maneira rápida e de se fazer o mesmo com JS puro.
Digamos, porém, que você quer simplificar ainda mais – algo assim:
render() {
// Registraos dados
console.log(this.state.myPosts);
const data = this.state.myPosts;
// Armazena o objeto aninhado que você quer acessar na variável posts
const posts = data.content;
// Registra com sucesso o objeto aninhado que eu quero acessar
console.log(posts);
// Erro, isto não me deixará passar a variável posts para Object.keys
const display = Object.keys(posts).map(key =>
<option value={key}>{posts[key]}</option>
)
return(
<div>
{display}
</div>
);
}
Você, porém, chega no erro TypeError: can't convert undefined to object error
sempre que tenta passar posts
para Object.keys
.
Lembre-se de que esse erro nada tem a ver com o React. Não é uma operação legl passar um objeto como filho de um componente.
Object.keys()
retorna apenas as chaves de objeto que são passadas como parâmetro. Você precisaria chamá-lo diversas vezes para percorrer todas as chaves aninhadas.
Se desejar exibir todo o objeto aninhado, uma opção é usar uma função que converta cada objeto em um componente do React e que o passe em um array:
let data= []
visit(obj, (val) => {
data.push(<p>{val}</p>) // envolve em um <p> qualquer tipo que não seja um objeto
})
...
return <UmComponenteQualquer> {data} </UmComponenteQualquer>
Pacotes úteis
Outra opção é usar um pacote como o json-query para ajudar a percorrer os dados em JSON aninhados.
Aqui temos uma versão modificada da função render
acima usando o json-query
:
render() {
const utopian = Object.keys(this.state.utopianCash);
console.log(this.state.utopianCash);
var author = jsonQuery('[*][author]', { data: this.state.utopianCash }).value
var title = jsonQuery('[*][title]', { data: this.state.utopianCash }).value
var payout = jsonQuery('[*][total_payout_value]', { data: this.state.utopianCash }).value
var postLink = jsonQuery('[*][url]', { data: this.state.utopianCash }).value
var pendingPayout = jsonQuery('[*][pending_payout_value]', { data: this.state.utopianCash }).value
var netVotes = jsonQuery('[*][net_votes]', { data: this.state.utopianCash }).value
let display = utopian.map((post, i) => {
return (
<div className="utopian-items">
<p>
<strong>Author: </strong>
{author[i]}
</p>
<p>
<strong>Title: </strong>
<a href={`https://www.steemit.com` + postLink[i]}>{title[i]}</a>
</p>
<p>
<strong>Pending Payout: </strong>
{pendingPayout[i]}
</p>
<p>
<strong>Votes: </strong>
{netVotes[i]}
</p>
</div>
);
});
return (
<div className="utopian-container">
{display}
<User />
</div>
);
}
}