├── .babelrc
├── .editorconfig
├── .gitignore
├── LICENSE
├── README.md
├── assets
└── test_performance.png
├── lib
└── zh_cn.json
├── package.json
├── src
├── entry.js
└── toggle.js
└── webpack.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "es2015",
4 | "react"
5 | ]
6 | }
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: http://editorconfig.org/#example-file
2 |
3 | # top-most EditorConfig file
4 | root = true
5 |
6 | [Makefile]
7 | indent_style = tab
8 |
9 | [*.{js,md,jsx}]
10 | charset = utf-8
11 | indent_style = space
12 | indent_size = 2
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /dist
2 | /node_modules
3 | # Created by .ignore support plugin (hsz.mobi)
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 龙天
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-datables-example
2 | Example for Datatables usage with React and Webpack
3 |
4 | > This example will mainly focus on how to use Datatables and its extensions in React project instead of diving into the [fabulous API](http://www.datatables.net/reference/api/).
5 |
6 | **How to run this example**
7 |
8 | ```sh
9 | npm install && npm start
10 | ```
11 |
12 | Open your browser and navigate to [http://localhost:8080/static/entry](http://localhost:8080/static/entry)
13 |
14 | ### How to import Datatables ?
15 |
16 | ```js
17 | import $ from 'jquery';
18 | import 'datatables.net';
19 | ```
20 |
21 | After being imported, you can initialize a table DOM element normally
22 |
23 | `$(elem).dataTable(options)` or `$(elem).DataTable(options)`
24 |
25 | ### Is it possible to use DataTable object directly ?
26 |
27 | No, because it is deeply coupled with jQuery, it requires jQuery context to intialize the table.
28 |
29 | In fact, the DataTable object is imported by default.
30 |
31 | ```js
32 | import $ from 'jquery';
33 | import DataTable from 'datatables.net';
34 | console.log(DataTable === $.fn.dataTable); // true
35 | ```
36 |
37 | ### How to import Bootstrap styling ?
38 |
39 | ```js
40 | import 'bootstrap/dist/css/bootstrap.css';
41 | import 'datatables.net-bs/js/dataTables.bootstrap';
42 | import 'datatables.net-bs/css/dataTables.bootstrap.css';
43 | ```
44 | Then, make sure you configure the `css-loader` and `style-loader` right in `webpack.config.js` file.
45 |
46 | ### How to load the i18n/fonts file asynchronously ?
47 |
48 | Extend the dataTable.defaults object
49 |
50 | ```js
51 | $.extend(true, $.fn.dataTable.defaults, {
52 | language: {
53 | url: require('../lib/zh_cn.json')
54 | }
55 | });
56 | ```
57 |
58 | Loading i18n files is just like loading icon font files.
59 |
60 | ```js
61 | {
62 | test : /\.(json|ttf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,
63 | loader : 'file-loader',
64 | query:{
65 | name:'[name]-[md5:hash:8].[ext]'
66 | }
67 | }
68 | ```
69 |
70 | ### How to load extensions ?
71 |
72 | Check the extensions list, https://www.npmjs.com/~datatables
73 |
74 | To import an extension, normaly it would require two steps as following:
75 |
76 | ```js
77 | import 'datatables.net-fixedheader';
78 | import 'datatables.net-fixedheader-bs/css/fixedHeader.bootstrap.css';
79 | ```
80 |
81 | 1\. Load the extension script, normaly the entry file is defined in package.json `main` property ;
82 |
83 | 2\. Load the style for specific front-end framework. For example, `bs` means `Bootstrap`.
84 |
85 | ### How to use string templates
86 |
87 | [ES6 template](https://github.com/esnext/es6-templates) is a good choice
88 |
89 | Data:
90 |
91 | ```js
92 | [
93 | {
94 | name:'1',
95 | foo:{
96 | bar:1
97 | }
98 | }
99 | ...
100 | ]
101 | ```
102 |
103 | Columns:
104 |
105 | ```js
106 | [
107 | {
108 | data:'foo',
109 | title:'foo.bar',
110 | render:foo=>`${foo.bar}`
111 | }
112 | ...
113 | }
114 | ```
115 |
116 | ### How to use React components in cell rendering
117 |
118 | Let's assume we need to use the `react-toggle` component in our table.
119 |
120 | We have three ways to use it:
121 |
122 | **Implement render function and use React DOM server's renderToStaticMarkup method**
123 |
124 | ```js
125 | {
126 | columns:[{
127 | ...
128 | render:elem=>renderToStaticMarkup()
129 | }]
130 | ...
131 | }
132 | ```
133 |
134 | **Implement datatable createdCell function and create multiple React roots**
135 |
136 | ```js
137 | {
138 | columns:[{
139 | ...
140 | createdCell:(td,val)=>render(,td)
141 | }]
142 | ...
143 | }
144 | ```
145 |
146 | **Prepare the HTML markup first and then initialize the Datatable**
147 | ```html
148 |
149 | {
150 | DATA.map(e=>
151 | {e.id} |
152 | {e.name} |
153 | {e.value} |
154 | |
155 |
)
156 | }
157 |
158 | ```
159 |
160 | A performance test page is written to show the difference between these ways
161 |
162 | [http://localhost:8080/static/toggle](http://localhost:8080/static/toggle)
163 |
164 | 
165 |
166 | And here is the performance report with 5000 data in table comparing above three options.
167 |
168 | |Option | Duration | usedJSHeapSize |
169 | |---------------------------|---------------------|------------------|
170 | | renderToStaticMarkup | 6384.74ms | 29.75M |
171 | | render | 4579.46ms | 103.95M |
172 | | markup | 6497.95ms | 189.78M |
173 |
174 | Summary:
175 |
176 | * If your component is dummy(e.g stateless funtion), use `renderToStaticMarkup`
177 | * If your component has state, but has no dependency in its context, use `render`
178 | * If your component has state or has dependency in its context (e.g Redux Componet or React Router Links), use `markup`
179 |
180 |
181 | ### TODO
182 |
183 | * [x] work with JSON data
184 | * [ ] Lifecycle
185 | * [x] Using React in column rendering
186 | * [ ] How to avoid conflicts between them
187 | * [x] Work with react-dom/server
--------------------------------------------------------------------------------
/assets/test_performance.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/longtian/react-datatables-example/b6634ac4cbebffc3595ff15bd534375bc70e8600/assets/test_performance.png
--------------------------------------------------------------------------------
/lib/zh_cn.json:
--------------------------------------------------------------------------------
1 | {
2 | "sProcessing": "处理中...",
3 | "sLengthMenu": "显示 _MENU_ 项结果",
4 | "sZeroRecords": "没有匹配结果",
5 | "sInfo": "显示第 _START_ 至 _END_ 项结果,共 _TOTAL_ 项",
6 | "sInfoEmpty": "显示第 0 至 0 项结果,共 0 项",
7 | "sInfoFiltered": "(由 _MAX_ 项结果过滤)",
8 | "sInfoPostFix": "",
9 | "sSearch": "搜索:",
10 | "sUrl": "",
11 | "sEmptyTable": "表中数据为空",
12 | "sLoadingRecords": "载入中...",
13 | "sInfoThousands": ",",
14 | "oPaginate": {
15 | "sFirst": "首页",
16 | "sPrevious": "上页",
17 | "sNext": "下页",
18 | "sLast": "末页"
19 | },
20 | "oAria": {
21 | "sSortAscending": ": 以升序排列此列",
22 | "sSortDescending": ": 以降序排列此列"
23 | }
24 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-datatables-example",
3 | "version": "0.0.1",
4 | "description": "Example for Datatable usage with React and Webpack",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "webpack-dev-server",
8 | "webpack": "rimraf dist && webpack"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/wyvernnot/react-datatables-example.git"
13 | },
14 | "author": "",
15 | "license": "MIT",
16 | "bugs": {
17 | "url": "https://github.com/wyvernnot/react-datatables-example/issues"
18 | },
19 | "homepage": "https://github.com/wyvernnot/react-datatables-example#readme",
20 | "devDependencies": {
21 | "babel-core": "^6.4.0",
22 | "babel-loader": "^6.2.1",
23 | "babel-preset-es2015": "^6.3.13",
24 | "babel-preset-react": "^6.3.13",
25 | "bootstrap": "^3.3.6",
26 | "css-loader": "^0.23.1",
27 | "datatables.net": "^1.10.10",
28 | "datatables.net-bs": "^1.10.10",
29 | "datatables.net-fixedheader": "^3.1.0",
30 | "datatables.net-fixedheader-bs": "^3.1.0",
31 | "file-loader": "^0.8.5",
32 | "history": "^1.17.0",
33 | "jquery": "^2.2.0",
34 | "react": "^0.14.6",
35 | "react-dom": "^0.14.6",
36 | "react-toggle": "^2.0.1",
37 | "rimraf": "^2.5.0",
38 | "style-loader": "^0.13.0",
39 | "webpack": "^1.12.10",
40 | "webpack-dev-server": "^1.14.0",
41 | "whatwg-fetch": "^0.10.1"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/entry.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by yan on 16-1-11.
3 | */
4 |
5 | import $ from 'jquery';
6 | import 'datatables.net';
7 |
8 | import 'bootstrap/dist/css/bootstrap.css';
9 | import 'datatables.net-bs/js/dataTables.bootstrap';
10 | import 'datatables.net-bs/css/dataTables.bootstrap.css';
11 |
12 | import 'datatables.net-fixedheader';
13 | import 'datatables.net-fixedheader-bs/css/fixedHeader.bootstrap.css';
14 |
15 | import zh_cn from '../lib/zh_cn.json';
16 |
17 | import React,{Component} from 'react';
18 | import {render} from 'react-dom';
19 |
20 | let elem = document.createElement('div');
21 | document.body.appendChild(elem);
22 |
23 | $.extend(true, $.fn.dataTable.defaults, {
24 | language: {
25 | url: zh_cn
26 | }
27 | });
28 |
29 | const App = ()=> {
30 | return
31 |
32 |
Zero Configuration
33 |
$(elem).DataTable()}>
34 |
35 |
36 |
37 | value
38 | |
39 |
40 |
41 |
42 |
43 | 2 |
44 |
45 |
46 | 1 |
47 |
48 |
49 | 1 |
50 |
51 |
52 |
53 |
54 |
JSON
55 |
$(elem).DataTable({
56 | data:[{
57 | name:'1',
58 | foo:{
59 | bar:1
60 | }
61 | },{
62 | name:'2',
63 | foo:{
64 | bar:2
65 | }
66 | }],
67 | columns:[{
68 | data:'name',
69 | title:'Name'
70 | },
71 | {
72 | data:'foo.bar',
73 | title:'foo.bar'
74 | },
75 | {
76 | data:'foo',
77 | title:'foo',
78 | render:foo=>`${foo.bar}`
79 | }]
80 | })}>
81 |
82 |
83 |
84 |
Fixed Header
85 |
$(elem).DataTable({
86 | fixedHeader:true
87 | })}>
88 |
89 |
90 |
91 | It will be fixed
92 | |
93 |
94 |
95 |
96 |
97 | 2 |
98 |
99 |
100 | 2 |
101 |
102 |
103 | 2 |
104 |
105 |
106 | 2 |
107 |
108 |
109 | 2 |
110 |
111 |
112 | 2 |
113 |
114 |
115 |
116 |
117 |
Feature
118 |
$(elem).dataTable()}>
119 |
120 |
121 | value |
122 |
123 |
124 |
125 |
126 | 1 |
127 |
128 |
129 | 2 |
130 |
131 |
132 | 3 |
133 |
134 |
135 | 4 |
136 |
137 |
138 | 5 |
139 |
140 |
141 | 6 |
142 |
143 |
144 | 7 |
145 |
146 |
147 | 8 |
148 |
149 |
150 | 9 |
151 |
152 |
153 | 10 |
154 |
155 |
156 | 11 |
157 |
158 |
159 |
160 |
161 |
$(elem).DataTable()} className="table table-striped">
162 |
163 |
164 | name |
165 |
166 |
167 |
168 |
169 | hello |
170 |
171 |
172 | world |
173 |
174 |
175 |
176 |
177 |
$(elem).DataTable()} data-paging={false}>
178 |
179 |
180 |
181 | value
182 | |
183 |
184 |
185 |
186 |
187 | 2 |
188 |
189 |
190 | 1 |
191 |
192 |
193 | 1 |
194 |
195 |
196 |
197 |
198 | }
199 |
200 | render(, elem);
201 |
--------------------------------------------------------------------------------
/src/toggle.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by yan on 16-1-11.
3 | */
4 | import React from 'react';
5 | import {render} from 'react-dom';
6 | import $ from 'jquery';
7 | import 'datatables.net';
8 | import 'bootstrap/dist/css/bootstrap.css';
9 | import 'datatables.net-bs/js/dataTables.bootstrap';
10 | import 'datatables.net-bs/css/dataTables.bootstrap.css';
11 | import Toggle from 'react-toggle';
12 | import 'react-toggle/style.css';
13 | import {renderToStaticMarkup}from 'react-dom/server';
14 |
15 | let domElement = document.createElement('div');
16 | document.body.appendChild(domElement);
17 |
18 | let {
19 | app='APP_render',
20 | count=5000
21 | } = require('qs').parse(location.search.slice(1));
22 |
23 | let DATA = [];
24 |
25 | for (var i = 1; i <= count; i++) {
26 | DATA.push({
27 | id: i,
28 | name: 'item-' + i,
29 | value: Math.random() - 0.5
30 | })
31 | }
32 |
33 | const APP_renderToStaticMarkup = ()=> {
34 | let opts = {
35 | data: DATA,
36 | columns: [{
37 | title: 'id',
38 | data: 'id'
39 | }, {
40 | title: 'name',
41 | data: 'name'
42 | }, {
43 | title: 'value',
44 | data: 'value'
45 | }, {
46 | title: 'id',
47 | data: 'value',
48 | render: elem=>renderToStaticMarkup()
49 | }]
50 | }
51 | return $(elem).dataTable(opts)}>
52 | }
53 |
54 | const APP_render = ()=> {
55 | let opts = {
56 | data: DATA,
57 | columns: [{
58 | title: 'id',
59 | data: 'id'
60 | }, {
61 | title: 'name',
62 | data: 'name'
63 | }, {
64 | title: 'value',
65 | data: 'value'
66 | }, {
67 | title: 'id',
68 | data: 'value',
69 | createdCell: (td, val)=>render(, td)
70 | }]
71 | }
72 | return $(elem).dataTable(opts)}>
73 | }
74 |
75 | const APP_markup = ()=> {
76 | return $(elem).dataTable()}>
77 |
78 |
79 | id |
80 | name |
81 | value |
82 | - |
83 |
84 |
85 |
86 | {
87 | DATA.map(e=>
88 | {e.id} |
89 | {e.name} |
90 | {e.value} |
91 | |
92 |
)
93 | }
94 |
95 |
96 | }
97 |
98 |
99 | const Wrapper = props=> {
100 | return
106 | }
107 |
108 | console.time(app);
109 | var reactElement = React.createElement(Wrapper, null, React.createElement(eval(app)));
110 | render(reactElement, domElement);
111 | console.timeEnd(app);
112 |
113 | console.log(console.memory.usedJSHeapSize / 1024 / 1024);
114 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | module.exports = {
3 | entry: {
4 | entry:path.join(__dirname, 'src', 'entry.js'),
5 | toggle:path.join(__dirname, 'src', 'toggle.js'),
6 | },
7 | output: {
8 | path: path.join(__dirname, 'dist'),
9 | publicPath: '/static/',
10 | filename: '[name].js'
11 | },
12 | module: {
13 | loaders: [{
14 | test: /\.js(x)?$/,
15 | loader: 'babel'
16 | }, {
17 | test: /.css$/,
18 | loader: 'style-loader!css-loader'
19 | },{
20 | test : /\.(json|ttf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,
21 | loader : 'file-loader',
22 | query:{
23 | name:'[name]-[md5:hash:8].[ext]'
24 | }
25 | }]
26 | }
27 | }
--------------------------------------------------------------------------------