Skip to content

Commit

Permalink
Merge pull request #3324 from marmelab/replace-papaparse
Browse files Browse the repository at this point in the history
[RFR] Replace papaparse with a lighter library
  • Loading branch information
fzaninotto authored Jun 11, 2019
2 parents 3c2e7e1 + a9b3b73 commit c4ff3da
Show file tree
Hide file tree
Showing 11 changed files with 455 additions and 44 deletions.
24 changes: 24 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,27 @@ Components deprecated in 2.X have been removed in 3.x. This includes:
* `ViewTitle` (use `Title` instead)
* `RecordTitle` (use `TitleForRecord` instead)
* `TitleDeprecated` (use `Title` instead)

## Replace papaparse with a lighter library

React-admin used to bundle the `papaparse` library for converting JSON to CSV. But 90% of the `papaparse` code is used to convert CSV to JSON.

We decided to replace it by a lighter library: [jsonexport](https://github.com/kauegimenes/jsonexport).

If you had custom exporter on `List` components, here's how to migrate:

```diff
-import { unparse as convertToCSV } from 'papaparse/papaparse.min';
+import jsonExport from 'jsonexport/dist';

-const csv = convertToCSV({
- data: postsForExport,
- fields: ['id', 'title', 'author_name', 'body']
-});
-downloadCSV(csv, 'posts');
+jsonExport(postsForExport, {
+ headers: ['id', 'title', 'author_name', 'body']
+}, (err, csv) => {
+ downloadCSV(csv, 'posts');
+});
```
22 changes: 11 additions & 11 deletions docs/List.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ By default, clicking this button will:

The columns of the CSV file match all the fields of the records in the `dataProvider` response. That means that the export doesn't take into account the selection and ordering of fields in your `<List>` via `Field` components. If you want to customize the result, pass a custom `exporter` function to the `<List>`. This function will receive the data from the `dataProvider` (after step 1), and replace steps 2-3 (i.e. it's in charge of transforming, converting, and downloading the file).

**Tip**: For CSV conversion, you can import [Papaparse](https://www.papaparse.com/), a CSV parser and stringifier which is already a react-admin dependency. And for CSV download, take advantage of react-admin's `downloadCSV` function.
**Tip**: For CSV conversion, you can import [jsonexport](https://github.com/kauegimenes/jsonexport#browser-import-examples), a CSV to JSON converter which is already a react-admin dependency. And for CSV download, take advantage of react-admin's `downloadCSV` function.

**Tip**: You may also remove the `<ExportButton>` by passing `false` to the `exporter` prop: `exporter={false}`

Expand All @@ -162,19 +162,19 @@ Here is an example for a Posts exporter, omitting, adding, and reordering fields
```jsx
// in PostList.js
import { List, downloadCSV } from 'react-admin';
import { unparse as convertToCSV } from 'papaparse/papaparse.min';
import jsonExport from 'jsonexport/dist';

const exporter = posts => {
const postsForExport = posts.map(post => {
const { postForExport, backlinks, author } = post; // omit backlinks and author
postForExport.author_name = post.author.name; // add a field
return postForExport;
});
const csv = convertToCSV({
data: postsForExport,
fields: ['id', 'title', 'author_name', 'body'] // order fields in the export
jsonExport(postsForExport, {
headers: ['id', 'title', 'author_name', 'body'] // order fields in the export
}, (err, csv) => {
downloadCSV(csv, 'posts'); // download as 'posts.csv` file
});
downloadCSV(csv, 'posts'); // download as 'posts.csv` file
})

const PostList = props => (
Expand All @@ -191,19 +191,19 @@ Here is an example for a Comments exporter, fetching related Posts:
```jsx
// in CommentList.js
import { List, downloadCSV } from 'react-admin';
import { unparse as convertToCSV } from 'papaparse/papaparse.min';
import jsonExport from 'jsonexport/dist';

const exporter = (records, fetchRelatedRecords) => {
fetchRelatedRecords(records, 'post_id', 'posts').then(posts => {
const data = records.map(record => ({
...record,
post_title: posts[record.post_id].title,
}));
const csv = convertToCSV({
data,
fields: ['id', 'post_id', 'post_title', 'body'],
jsonExport(data, {
headers: ['id', 'post_id', 'post_title', 'body'],
}, (err, csv) => {;
downloadCSV(csv, 'comments');
});
downloadCSV(csv, 'comments');
});
};

Expand Down
4 changes: 3 additions & 1 deletion examples/demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@
"react-scripts": "^3.0.0",
"recompose": "~0.26.0",
"redux-form": "~8.2.0",
"redux-saga": "^1.0.0"
"redux-saga": "^1.0.0",
"source-map-explorer": "^2.0.0"
},
"scripts": {
"analyze": "source-map-explorer 'build/static/js/*.js'",
"start": "react-scripts start",
"build": "react-scripts build",
"eject": "react-scripts eject"
Expand Down
1 change: 1 addition & 0 deletions examples/simple/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"style-loader": "~0.20.3",
"wait-on": "^3.2.0",
"webpack": "~4.5.0",
"webpack-bundle-analyzer": "^3.3.2",
"webpack-cli": "~2.0.13",
"webpack-dev-server": "~3.1.11"
},
Expand Down
12 changes: 9 additions & 3 deletions examples/simple/src/comments/CommentList.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import CardHeader from '@material-ui/core/CardHeader';
import Grid from '@material-ui/core/Grid';
import Toolbar from '@material-ui/core/Toolbar';
import { makeStyles } from '@material-ui/core/styles';
import { unparse as convertToCSV } from 'papaparse/papaparse.min';
import jsonExport from 'jsonexport/dist';
import {
DateField,
EditButton,
Expand Down Expand Up @@ -47,15 +47,21 @@ const exporter = (records, fetchRelatedRecords) =>
recordForExport.post_title = posts[record.post_id].title;
return recordForExport;
});
const fields = [
const headers = [
'id',
'author_name',
'post_id',
'post_title',
'created_at',
'body',
];
downloadCSV(convertToCSV({ data, fields }), 'comments');

jsonExport(data, { headers }, (error, csv) => {
if (error) {
console.error(error);
}
downloadCSV(csv, 'comments');
})
});

const CommentPagination = ({
Expand Down
7 changes: 5 additions & 2 deletions examples/simple/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
const IgnoreNotFoundExportPlugin = require('ignore-not-found-export-plugin');

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
devtool: 'cheap-module-source-map',
module: {
Expand Down Expand Up @@ -31,7 +32,9 @@ module.exports = {
'RedirectionSideEffect',
'RefreshSideEffect',
]),
],
].concat(
process.env.NODE_ENV === 'development' ? [new BundleAnalyzerPlugin()] : [],
),
resolve: {
extensions: ['.ts', '.js', '.tsx', '.json'],
alias: {
Expand Down
2 changes: 1 addition & 1 deletion packages/ra-ui-materialui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@
"classnames": "~2.2.5",
"connected-react-router": "^6.4.0",
"inflection": "~1.12.0",
"jsonexport": "^2.4.1",
"lodash": "~4.17.5",
"material-ui-chip-input": "1.0.0-beta.6 - 1.0.0-beta.8",
"papaparse": "^4.1.4",
"prop-types": "^15.6.1",
"ra-core": "^2.9.1",
"react-autosuggest": "^9.4.2",
Expand Down
4 changes: 2 additions & 2 deletions packages/ra-ui-materialui/src/button/ExportButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import GetApp from '@material-ui/icons/GetApp';
import { crudGetAll, downloadCSV, CRUD_GET_MANY, GET_MANY } from 'ra-core';
import { unparse as convertToCSV } from 'papaparse/papaparse.min';
import jsonExport from 'jsonexport/dist';

import Button from './Button';

Expand Down Expand Up @@ -133,7 +133,7 @@ class ExportButton extends Component {
fetchRelatedRecords(dispatch),
dispatch
)
: downloadCSV(convertToCSV(data), resource)
: jsonExport(data, (err, csv) => downloadCSV(csv, resource))
)
);

Expand Down
4 changes: 2 additions & 2 deletions packages/ra-ui-materialui/src/list/List.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,14 +118,14 @@ export const ListView = withStyles(styles)(
children,
className,
classes,
component,
component :Content,
exporter,
title,
...rest
}) => {
const { defaultTitle, version } = rest;
const controllerProps = getListControllerProps(rest);
const Content = component;

return (
<div
className={classnames('list-page', classes.root, className)}
Expand Down
2 changes: 1 addition & 1 deletion packages/ra-ui-materialui/src/list/ListActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const ListActions = ({
)}
</TopToolbar>
),
[resource, displayedFilters, filterValues, selectedIds, filters]
[resource, displayedFilters, filterValues, selectedIds, filters, total]
);

ListActions.propTypes = {
Expand Down
Loading

0 comments on commit c4ff3da

Please sign in to comment.