Search code examples
reactjsstatereact-state-managementreact-state

ReactJS iterate through state array with dynamic key with its data


I have a array like:

"data": {
    "key1": {
        "key_val1": "data1",
        "key_val2": "data2",
        "key_val3": "data3",
        "key_val4": "data4",

    },
    "key2": {
        "key_val1": "data1",
        "key_val2": "data2",
        "key_val3": "data3",
        "key_val4": "data4",

    },
    "key3": {
        "key_val1": "data1",
        "key_val2": "data2",
        "key_val3": "data3",
        "key_val4": "data4",

    }
}

I want to iterate with this array and populate my table, but I am getting error this.state.data.map() is not a function.

Here is my react component:

import React, { Component } from "react";
import $ from "jquery";
import axios from "axios";

class DataComponent extends Component {
  state = {
    data: [],
    isLoading: true
  };
  componentDidMount() {
    this.getData();
  }

  getData() {
    var curr_date = new Date().toISOString().substring(0, 10);
    const params = new FormData();
    params.append("date", curr_date);
    axios
      .post(`http://api.mydomain.in/getdataList/`, params)
      .then(res => {
        if (res.data.result === 1) {
          this.setState({
            data: res.data.data,
            isLoading: false
          });
        }
        if (!this.state.isLoading) {
          this.initTable();
        }

      })
      .catch(err => {
        console.log(err);
      });
  }

  initTable() {
    $("#my_table").DataTable({
      bLengthChange: false,
      lengthMenu: [[15, 15, 15, "All"]],
      language: {
        search: "_INPUT_",
        searchPlaceholder: "Search records"
      }
    });
  }

  render() {
    return (
      <div className="tab-pane active" id="mytab">
        <div className="inner-content table-responsive">
          <table id="my_table" className="display" style={{ width: "100%" }}>
            <thead>
              <tr className="heading-table">
                <th>Col1</th>
                <th>col2 </th>
                <th>col3 </th>
                <th>col4 </th>
                <th>col5 </th>
              </tr>
            </thead>
            {this.state.isLoading ? null : (
              <tbody>
                {this.state.data.map(d => (
                  <tr>
                    <td>{d}</td>
                    <td>{d.key_val1}</td>
                    <td>{d.key_val2}</td>
                    <td>{d.key_val3}</td>
                    <td>{d.key_val4}</td>
                  </tr>
                ))}
              </tbody>
            )}
          </table>
        </div>
      </div>
    );
  }
}

export default DataComponent;

this.state.data will be updated with the sample array as shown above.

How can I achieve this? I have also tried with Object.keys(this.state.data).map(), but no luck.

Thanks in advance.


Solution

  • Your data variable is a JSON object, not an array. To convert it to an array and get both the keys and values, you can use Object.entries.

    This function will return an array containing other arrays with the key as your first value, and the value as the second :

    {Object.entries(this.state.data).map(([key, value]) => (
        <tr key={key}>
            <td>{key}</td>
            <td>{value.key_val1}</td>
            <td>{value.key_val2}</td>
            <td>{value.key_val3}</td>
            <td>{value.key_val4}</td>
        </tr>
    ))}
    

    If you do not need the keys of your object, you can use Object.values.

    An alternative using deconstruction :

    {Object.entries(this.state.data).map(([key, { key_val1, key_val2, key_val3, key_val4 }]) => (
        <tr key={key}>
            <td>{key}</td>
            <td>{key_val1}</td>
            <td>{key_val2}</td>
            <td>{key_val3}</td>
            <td>{key_val4}</td>
        </tr>
    ))}
    

    Another version using nested mapping, that will work with any kind of objects :

    {Object.entries(this.state.data).map(([key, value]) => (
        <tr key={key}>
            <td>{key}</td>
            {Object.entries(value).map(([name, data]) => <td key={name} >{data}</td>)}
        </tr>
    ))}