A few months ago, I was looking for a React component to display a table of data in one of our web applications in Undertone. In the previous project, which was not based on a high-level library, such as React, we used the DataTables jQuery plugin, and we were very pleased with the flexibility it offers. Now I was looking for something similar, which can be easily integrated as a React-component into our new application.
Having done quite extensive research, I learned two things. First, it’s not recommended to use DataTables with React, because both libraries control the DOM. Secondly, there was no library that provided the flexibility needed for our web application (since my research). The deadline for the delivery of the project was approaching, and I had to choose something. I decided to go the unpopular way and integrate DataTables into my project. The result was much better than I expected, and the whole integration process was actually quite smooth. In the following sections, I will describe the design of the project in which the integration of React and DataTables works.
We will use the Create-React-App (CRA) for the scaffolding of our project. If you are not familiar with CRA, this is basically a tool for building React applications without configuration. The first thing we need to do is install the CRA (if you haven't already installed it) and initialize the new project using the create-react-app
(see the documentation for the CRA for details on how to initialize and run the project).
We complete our installation by adding the jQuery and Datatables modules as dev-dependencies.
$ npm i --save-dev datatables.net jquery
After the installation of the modules is completed, we delete unnecessary files automatically generated by CRA from the repository. The final result of the project that we create here in this article can be found in the react-datatables repository on GitHub.
We implement a very simple interface. Users have an input area where they can enter a name and an alias in the appropriate text fields. When the “Add” button is pressed, the data entered in the text fields are added to the data table. The name is a unique “key” for each entry — if the user types a name that already exists, the new pair of names is not created, and only the alias of the existing pair is updated in the table.
We already have the source files with empty content created by CRA. We create a new file containing our Table component - this component is responsible for the visualization and control of the table. First, we import both jQuery and DataTables and link them so that DataTables has access to the jQuerey functions. This can be done using the following two lines:
const $ = require('jquery'); $.DataTable = require('datatables.net');
We define two columns that extract the corresponding values from the nickname pair, which we are going to provide to the table:
const columns = [ { title: 'Name', width: 120, data: 'name' }, { title: 'Nickname', width: 180, data: 'nickname' }, ];
Finally, the definition of the component itself:
class Table extends Component { componentDidMount() { $(this.refs.main).DataTable({ dom: '<"data-table-wrapper"t>', data: this.props.names, columns, ordering: false, }) } componentWillUnmount() { $('.data-table-wrapper') .find('table') .DataTable() .destroy(true) } shouldComponentUpdate(nextProps) { if (nextProps.names.length !== this.props.names.length) { reloadTableData(nextProps.names) } else { updateTable(nextProps.names) } return false } render() { return ( <div> <table ref="main" /> </div> ) } }
A few comments. In the render
function, we create one HTML table element. This is a DataTables requirement, since a table element is needed to populate the DOM. React will never know that there will be more DOM inside the table element. We guarantee that no further attempts will be made by React, always returning false
from the shouldComponentUpdate
method. The initialization of the table itself should occur only once, when our component is mounted, because we want to leave all the internal DOM manipulations in DataTables. Finally, we need to destroy the table when the component needs to be unmounted. This is done in the componentWillUnmount
method with a corresponding call to the DataTables API.
Currently, our Table component takes its data through props.names
, but we have not implemented a way to add or update names. We will do this in the next section.
There are two ways to change an array of name-nick pairs. First, add a new nickname to it. Secondly, replacing the existing pair of nickname with a new pair with the same name and another nickname. This is done in another component external to the Table component. (I will not go into details on how these updates are distributed - see the project repository on GitHub for more details.) Here we will look at two types of updates using two different methods.
1) When a new nickname pair is added, we reload the entire table:
function reloadTableData(names) { const table = $('.data-table-wrapper').find('table').DataTable() table.clear() table.rows.add(names) table.draw() }
We use the standard jQuery selector to find an instance of the table using the class we provided in componentDidMount
(data-table-wrapper). Then we delete all previous data and load new data (for brevity, I did not add a new nickname pair - this also works if you delete one pair or several pairs).
2) When the pair is updated, we want to find this pair and change the specific part of the data that has been changed (the nickname field in our example):
function updateTable(names) { const table = $('.data-table-wrapper').find('table').DataTable() let dataChanged = false table.rows().every(function() { const oldNameData = this.data() const newNameData = names.find(nameData => { return nameData.name === oldNameData.name }) if (oldNameData.nickname !== newNameData.nickname) { dataChanged = true this.data(newNameData) } return true }) if (dataChanged) { table.draw() } }
We need to remember to redraw the table using the API method draw
, if any data has been changed or the changes will not be visible to the user.
It remains only to distinguish between two cases and call the appropriate method when the table component is displayed in React. We do this in the shouldComponentUpdate
code to check the type of change and execute the corresponding method.
shouldComponentUpdate(nextProps) { if (nextProps.names.length !== this.props.names.length) { reloadTableData(nextProps.names) } else { updateTable(nextProps.names) } return false }
Now, every time the number of pairs in the props.names files changes, we re-display the entire table or update the corresponding elements. Note that in a real application, the contents of the entire array may change, and the number of elements may remain unchanged - a case that cannot occur in our example.
This is definitely possible and even fairly easy to integrate React and DataTables, if you protect React from DataTables manipulations in the DOM. The use case that we have implemented in this article is quite simple, but it provides all the building blocks necessary for the integration of work in a real project.
Please share your experience - what are the React-components used to implement the functionality of tables with sorting and pagination for manipulating large amounts of data?
Source: https://habr.com/ru/post/330656/