How to create a sortable table in React? How to access class method from the sorted objects?

﹥>﹥吖頭↗ 提交于 2020-01-15 05:17:05

问题


I have an array of objects. i want to access class method inside object function property. How to implement this? please help me. I wrote smth like this:

render() {       
  tableTh = [
    {name: i18n.t('ExtendedModalBar.naming'), style:{},  handleSort () {}},
      ...
  ]
  ...
}

I am trying to write someth like this:

class Table extends Component {

  handleSort() {

  }
  ...

  render() {
  }

}

My table headers are formed dynamically:

<thead>
  <tr>
    <th style={{ width: "50px" }}>
      <Checkbox
        checked={this.state.selectedAll}
        onChange={() => this.selectAll()}
      />
    </th>
    {tableTh.map((obj, index) => {
      return (
        <th key={index.toString()} style={obj.style} onClick={obj.handleSort}>
          {obj.name}
        </th>
      );
    })}
  </tr>
</thead>        

I must implement sorting of table columns. If tables are formed statically it is easy to attach onclick to th tag. but if code was written like this, i am stuck. How to write method in the class that will be accessible from inside property of objects (array of objects) ? And I need sorting in both directions ascending and descending. My table column should change own sorting direction when I click on its table header. Any answers will be taken into acccount. Thanks in advance


回答1:


The problem with having the state-changing function on the state itself is that this inherently makes it difficult to manipulate the state.

var data = [
  { name: 'a', change: /* ??? */ },
  { name: 'b', change: /* ??? */ },
  { name: 'c', change: /* ??? */ }
]

// How do we write the function ??? to change the order of data?

It is much simpler to lift the data-manipulation logic at a higher level (outside of the data itself).

In this case, we would put the data-manipulation logic on the class itself as opposed to within each item.

We can do this by leveraging the state in React.

A state is used to hold any (dynamic) presentational (rendered) data for a component. An example of dynamic (changing) presentational data would be a table that sorts its columns.

The reason we want to put presentational data in the state is because React allows us to re-trigger the presentation (by re-rendering) to always show the latest values for our data.

We can achieve this just by changing the data itself and not any presentational logic. This is why React component structure is referred to as declarative.

Anytime the state is updated, React will call the render function to obtain the component structure with the latest state changes and display those on the appropriate medium (in your case the DOM)

Here's one way to incorporate state to create a sortable table:

class Table extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      sortDirection: "asc", // we start with ascending order by default
      selectedHeaderIndex: 0 // we start by sorting based on the first header (the one in position 0)
    };

    this.ascComparator = (row1, row2) =>
      row1[this.state.selectedHeaderIndex].localeCompare(
        row2[this.state.selectedHeaderIndex]
      );

    this.descComparator = (row1, row2) =>
      row2[this.state.selectedHeaderIndex].localeCompare(
        row1[this.state.selectedHeaderIndex]
      );

    this.flipSortDirection = () =>
      this.state.sortDirection === "asc" ? "desc" : "asc";
  }

  render() {
    const { headers, rows } = this.props.table;

    const comparator =
      this.state.sortDirection === "asc"
        ? this.ascComparator
        : this.descComparator;

    // sort the rows based on the selected header
    const sortedRows = rows.sort(comparator);

    return (
      <table>
        <thead>
          {headers.map((header, i) => (
            <th
              onClick={() => {
                this.setState({
                  // if we clicked on the already selected index, we flip the sort direction
                  sortDirection:
                    this.state.selectedHeaderIndex === i
                      ? this.flipSortDirection()
                      : "asc",
                  selectedHeaderIndex: i
                });
              }}
            >
              {header}
            </th>
          ))}
        </thead>
        <tbody>
          {sortedRows.map(row => (
            <tr>
              {row.map(cell => (
                <td>{cell}</td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>
    );
  }
}

const table = {
  headers: ["h1", "h2", "h3"],
  rows: [["a", "9", "+"], ["b", "6", "-"], ["c", "3", "="]]
};

ReactDOM.render(<Table table={table} />, document.querySelector("#app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>

Using hooks the code becomes a bit more readable (click for the CodeSandbox example since SO doesn't support React 16.8 yet):

function Table({ table }) {
  const { headers, rows } = table;

  const [selectedHeaderIndex, setSelectedHeaderIndex] = React.useState(0); // we start by sorting based on the first header (the one in position 0)
  const [sortDirection, setSortDirection] = React.useState("asc"); // we start with ascending order by default

  // determine the sorting comparator based on the sorting direction
  const comparator =
    sortDirection === "asc"
      ? (row1, row2) =>
          row1[selectedHeaderIndex].localeCompare(row2[selectedHeaderIndex])
      : (row1, row2) =>
          row2[selectedHeaderIndex].localeCompare(row1[selectedHeaderIndex]);

  const flipSortDirection = () => (sortDirection === "asc" ? "desc" : "asc");

  // sort the rows based on the selected header
  const sortedRows = rows.sort(comparator);

  return (
    <table>
      <thead>
        {headers.map((header, i) => (
          <th
            onClick={() => {
              setSelectedHeaderIndex(i);
              setSortDirection(
                selectedHeaderIndex === i ? flipSortDirection() : "asc"
              );
            }}
          >
            {header}
          </th>
        ))}
      </thead>
      <tbody>
        {sortedRows.map(row => (
          <tr>
            {row.map(cell => (
              <td>{cell}</td>
            ))}
          </tr>
        ))}
      </tbody>
    </table>
  );
}

const table = {
  headers: ["h1", "h2", "h3"],
  rows: [["a", "9", "+"], ["b", "6", "-"], ["c", "3", "="]]
};

ReactDOM.render(<Table table={table} />, document.querySelector("#root"));



回答2:


Change the click handler of the header from onClick={obj.handleSort} to onClick={e => handleSort(columnName, sortDirection). Values of columnName and sortedDirection are passed down from parent component

handleSort in Table component will be able to accept parameter columnName and sortDirection to manipulate data.

Check out this sample implementation

https://codesandbox.io/embed/6lmk0q3w5z?fontsize=14



来源:https://stackoverflow.com/questions/55015470/how-to-create-a-sortable-table-in-react-how-to-access-class-method-from-the-sor

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!