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>
    );
  }
}