├── .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 | ![performance](assets/test_performance.png) 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 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 |
37 | value 38 |
2
1
1
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 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 |
91 | It will be fixed 92 |
2
2
2
2
2
2
116 | 117 | Feature 118 | $(elem).dataTable()}> 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 |
value
1
2
3
4
5
6
7
8
9
10
11
160 | 161 | $(elem).DataTable()} className="table table-striped"> 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 |
name
hello
world
176 | 177 | $(elem).DataTable()} data-paging={false}> 178 | 179 | 180 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 |
181 | value 182 |
2
1
1
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 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | { 87 | DATA.map(e=> 88 | 89 | 90 | 91 | 92 | ) 93 | } 94 | 95 |
idnamevalue-
{e.id}{e.name}{e.value}
96 | } 97 | 98 | 99 | const Wrapper = props=> { 100 | return
101 | APP_renderToStaticMarkup | 102 | APP_render | 103 | APP_markup 104 | {props.children} 105 |
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 | } --------------------------------------------------------------------------------