Search code examples
javascriptreactjsecmascript-6setstatereact-table

Implementing Show More/ Show Less for table present in a cell: ReactJS


I am trying to implement a table for nested array of objects and I want to display it in a cell of that column like a table with Show More/Show Less functionality.

Sandbox : https://codesandbox.io/s/react-table-row-table-g8ws3

Also I am able to bring the values as table data, but I want to add this table data dynamically with Show more / Show less

Want the output something like with show more if items are more than 2 and then followed by the toggle for show less

I am using react table v6 for this application

+-----------+-----------+-----+-----------------+
| firstname | status    | age | nested          |
+-----------+-----------+-----+-----------------+
| Jack      | Submitted | 14  | name  value     |
|           |           |     | -----------     |
|           |           |     | test1  NA       |
|           |           |     |                 |
|           |           |     | test2  NA       |
|           |           |     |                 |
|           |           |     |  Show More/Less |
+-----------+-----------+-----+-----------------+
| Simon     | Pending   | 15  | name  value     |
|           |           |     |                 |
|           |           |     | -----------     |
|           |           |     |                 |
|           |           |     | test3  NA       |
|           |           |     |                 |
|           |           |     |                 |
|           |           |     | test4  Go       |
|           |           |     |                 |
|           |           |     | Show More/Less  |
+-----------+-----------+-----+-----------------+
import * as React from "react";
import { render } from "react-dom";
import DataGrid from "./DataGrid";

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      data: [],
      columns: [],
    };
  }

  componentDidMount() {
    this.getData();
    this.getColumns();
  }

  getData = () => {
    const data = [
      {
        firstName: "Jack",
        status: "Submitted",
        nested: [
          {
            name: "test1",
            value: "NA"
          },
          {
            name: "test2",
            value: "NA"
          }
        ],
        age: "14"
      },
      {
        firstName: "Simon",
        status: "Pending",
        nested: [
          {
            name: "test3",
            value: "NA"
          },
          {
            name: "test4",
            value: "Go"
          }
        ],
        age: "15"
      }
    ];
    this.setState({ data });
  };

  getColumns = () => {
    const columns = [
      {
        Header: "First Name",
        accessor: "firstName"
      },
      {
        Header: "Status",
        accessor: "status"
      },
      {
        Header: "Age",
        accessor: "age"
      },
      {
        Header: "Nested",
        id: "nested",
        accessor: data =>
          data.nested.map(item => (
            <div>
              <span style={{ marginRight: "10px" }}>{item.name}</span>
              <span>{item.value}</span>
            </div>
          ))
      }
    ];
    this.setState({ columns });
  };

  render() {
    return (
      <>
        <DataGrid
          data={this.state.data}
          columns={this.state.columns}
        />
      </>
    );
  }
}


Solution

  • first you need after setting the data into the state to decide which amount of data will be rendered and the default state will be the two elements with the button "show more", so, for that, you need to trigger a state for is the button clicked or not and depending on this state the rendered items should be, for inserting button to the table, you need to insert it as the last child to the array without filling the columns just the button in the end of the list

    and for nested show more inside the nested column, we need like the same logic but for smaller component, for that, we need to separate it to let it take its own state

    This was my attempt:

    import * as React from "react";
    import { render } from "react-dom";
    import DataGrid from "./DataGrid";
    import Nested from "./Nested.js";
    
    class App extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          items: [],
          data: [],
          columns: [],
          clicked: false,
          text: "show more"
        };
      }
    
      componentDidMount() {
        this.getData();
        this.getColumns();
      }
    
      onClick = () => {
        this.setState(
          prevState => {
            const text = !prevState.clicked ? "show less" : "show more";
            return {
              clicked: !prevState.clicked,
              text
            };
          },
          () => this.formatData()
        );
      };
    
      getData = () => {
        const data = [
          {
            firstName: "Jack",
            status: "Submitted",
            nested: [
              {
                name: "test1",
                value: "NA"
              },
              {
                name: "test2",
                value: "NA"
              },
              {
                name: "test5",
                value: "NA"
              }
            ],
            age: "14"
          },
          {
            firstName: "Simon",
            status: "Pending",
            nested: [
              {
                name: "test3",
                value: "NA"
              },
              {
                name: "test4",
                value: "Go"
              },
              {
                name: "test6",
                value: "NA"
              },
              {
                name: "test7",
                value: "NA"
              }
            ],
            age: "15"
          },
          {
            firstName: "Simon2",
            status: "Pending",
            nested: [
              {
                name: "test3",
                value: "NA"
              },
              {
                name: "test4",
                value: "Go"
              }
            ],
            age: "15"
          }
        ];
        this.setState(() => ({ data }), () => this.formatData());
      };
    
      getColumns = () => {
        const columns = [
          {
            Header: "First Name",
            accessor: "firstName"
          },
          {
            Header: "Status",
            accessor: "status"
          },
          {
            Header: "Age",
            accessor: "age"
          },
          {
            Header: "Nested",
            id: "nested",
            accessor: data => <Nested data={data.nested} />
          }
        ];
        this.setState({ columns });
      };
    
      formatData = () => {
        const clickBtn = {
          firstName: <button onClick={this.onClick}>{this.state.text}</button>
        };
        if (this.state.clicked) {
          const items = [...this.state.data, clickBtn];
          this.setState({ items });
        } else {
          const items = [...this.state.data.slice(0, 2), clickBtn];
          this.setState({ items });
        }
      };
    
      render() {
        return (
          <>
            <DataGrid data={this.state.items} columns={this.state.columns} />
          </>
        );
      }
    }
    
    render(<App />, document.getElementById("root"));
    
    
    

    and the Nested file :

    import * as React from "react";
    
    class Nested extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          items: [],
          data: [],
          clicked: false,
          text: "show more"
        };
      }
    
      componentDidMount() {
        this.setState(() => ({ data: this.props.data }), () => this.formatData());
      }
    
      onClick = () => {
        this.setState(
          prevState => {
            const text = !prevState.clicked ? "show less" : "show more";
            return {
              clicked: !prevState.clicked,
              text
            };
          },
          () => this.formatData()
        );
      };
    
      formatData = () => {
        console.log(this.state.data);
    
        const clickBtn = {
          type: "btn",
          component: <button onClick={this.onClick}>{this.state.text}</button>
        };
        if (this.state.clicked) {
          const items = [...this.state.data, clickBtn];
          this.setState({ items });
        } else {
          const items = this.state.data && [
            ...this.state.data.slice(0, 2),
            clickBtn
          ];
          this.setState({ items });
        }
      };
    
      render() {
        const { items } = this.state;
        return (
          <>
            {items &&
              items.map(item =>
                item.type === "btn" ? (
                  !!item.component && item.component
                ) : (
                  <div>
                    <span style={{ marginRight: "10px" }}>{item.name}</span>
                    <span>{item.value}</span>
                  </div>
                )
              )}
          </>
        );
      }
    }
    
    export default Nested;
    
    

    Sandbox: https://codesandbox.io/s/react-table-row-table-zpiyo