A csv file import button for react-admin

Last update: May 2, 2022

react-admin-import-csv

NPM Version Downloads/week License Github Issues Build and Publish Code Coverage

CSV import button for the react-admin framework.

image

Usage

Simply import the button into a toolbar, like so:

Basic Import Action Only

{ const { className, basePath } = props; return ( ); }; export const PostList = (props) => ( } actions={}> ... ); ">
import {
  Datagrid,
  List,
  TextField,
  RichTextField,
  TopToolbar,
} from "react-admin";
import { ImportButton } from "react-admin-import-csv";
import { CreateButton } from "ra-ui-materialui";

const ListActions = (props) => {
  const { className, basePath } = props;
  return (
    <TopToolbar className={className}>
      <CreateButton basePath={basePath} />
      <ImportButton {...props} />
    </TopToolbar>
  );
};
export const PostList = (props) => (
  <List {...props} filters={<PostFilter />} actions={<ListActions />}>
    <Datagrid>
      <TextField source="title" />
      <RichTextField source="body" />
      ...
    </Datagrid>
  </List>
);

Export/Import Actions

{ const { className, basePath, total, resource, currentSort, filterValues, exporter, } = props; return ( ); }; export const PostList = (props) => ( } actions={}> ... ); ">
import {
  Datagrid,
  List,
  TextField,
  RichTextField,
  TopToolbar,
} from "react-admin";
import { ImportButton } from "react-admin-import-csv";
import { CreateButton, ExportButton } from "ra-ui-materialui";

const ListActions = (props) => {
  const {
    className,
    basePath,
    total,
    resource,
    currentSort,
    filterValues,
    exporter,
  } = props;
  return (
    <TopToolbar className={className}>
      <CreateButton basePath={basePath} />
      <ExportButton
        disabled={total === 0}
        resource={resource}
        sort={currentSort}
        filter={filterValues}
        exporter={exporter}
      />
      <ImportButton {...props} />
    </TopToolbar>
  );
};
export const PostList = (props) => (
  <List {...props} filters={<PostFilter />} actions={<ListActions />}>
    <Datagrid>
      <TextField source="title" />
      <RichTextField source="body" />
      ...
    </Datagrid>
  </List>
);

Configuration Options

Promise ; // A function to handle row errors after import postCommitCallback?: (error: any) => void; // Transform rows before anything is sent to dataprovider transformRows?: (csvRows: any[]) => Promise ; // Async function to Validate a row, reject the promise if it's not valid validateRow?: (csvRowItem: any) => Promise ; // Any option from the "papaparse" library parseConfig?: { // For all options see: https://www.papaparse.com/docs#config } } ">
// All configuration options are optional
const config: ImportConfig = {
  // Enable logging
  logging?: boolean;
  // Disable "import new" button
  disableImportNew?: boolean;
  // Disable "import overwrite" button
  disableImportOverwrite?: boolean;
  // A function to translate the CSV rows on import
  preCommitCallback?: (action: "create" | "overwrite", values: any[]) => Promise<any[]>;
  // A function to handle row errors after import
  postCommitCallback?: (error: any) => void;
  // Transform rows before anything is sent to dataprovider
  transformRows?: (csvRows: any[]) => Promise<any[]>;
  // Async function to Validate a row, reject the promise if it's not valid
  validateRow?: (csvRowItem: any) => Promise<void>;
  // Any option from the "papaparse" library
  parseConfig?: {
    // For all options see: https://www.papaparse.com/docs#config
  }
}
<ImportButton {...props} {...config}/>

Handle id fields which might be numbers

Use the paparse configuration option dynamicTyping

const importOptions = {
  parseConfig?: {
    // For all options see: https://www.papaparse.com/docs#config
    dynamicTyping: true
  }
}

Reducing Requests (.createMany() and .updateMany())

Your dataprovider will need to implement the .createMany() method in order to reduce requests to your backend. If it doesn't exist, it will fallback to calling .create() on all items, as shown below (same goes for .update()):

Name Special Method Fallback Method
Creating from CSV .createMany() .create()
Updating from CSV .updateManyArray() .update()

Interfaces

The dataprovider should accept these param interfaces for the bulk create/update methods.

export interface UpdateManyArrayParams {
  ids: Identifier[];
  data: any[];
}
export interface CreateManyParams {
  data: any[];
}

Example Implementation

Here's a quick example of how to implement .createMany() and .updateMany() in your dataprovider:

// Must be react-admin 3.x
const dataProviderWrapped = {
  ...dataProvider, // <- Your data provider
  createMany: async (resource, params) => {
    const items = params.data;
    // Handle create many here
  },
  updateMany: async (resource, params) => {
    const items = params.data;
    const idsToUpdate = params.ids;
    // Handle update many here
  }
}

// Pass into to other parts of the system as normal
return (
  <Admin dataProvider={dataProviderWrapped}

Translation i18n

This package uses react-admin's global i18n translation. Below is an example on how to set it up with this package.

Current supported languages (submit a PR if you'd like to add a language):

  • English (en)
  • Spanish (es)
  • Chinese (zh)
  • German (de)
  • French (fr)
  • Brazilian Portuguese (ptBR)
  • Russian (ru)

Example (i18nProvider)

(messages[locale] ? messages[locale] : messages.en), locale ); // declare prop in Admin component ">
import { resolveBrowserLocale, useLocale } from "react-admin";
import polyglotI18nProvider from "ra-i18n-polyglot";
import englishMessages from "ra-language-english";
// This package's translations
import * as domainMessages from "react-admin-import-csv/lib/i18n";

// Select locale based on react-admin OR browser
const locale = useLocale() || resolveBrowserLocale();
// Create messages object
const messages = {
  // Delete languages you don't need
  en: { ...englishMessages, ...domainMessages.en },
  zh: { ...chineseMessages, ...domainMessages.zh },
  es: { ...spanishMessages, ...domainMessages.es },
};
// Create polyglot provider
const i18nProvider = polyglotI18nProvider(
  (locale) => (messages[locale] ? messages[locale] : messages.en),
  locale
);

// declare prop in Admin component
<Admin dataProvider={dataProvider} i18nProvider={i18nProvider}>

More information on this setup here

Development

If you'd like to develop on react-admin-import-csv do the following.

Local install

  • git clone https://github.com/benwinding/react-admin-import-csv/
  • cd react-admin-import-csv
  • yarn

Tests

  • yarn test # in root folder

Run demo

Open another terminal

  • yarn build-watch

Open another terminal and goto the demo folder

  • yarn start

GitHub

https://github.com/benwinding/react-admin-import-csv
Comments
  • 1. Notification update - translation not working properly %{fname}

    According to this answer, the setState won't change the function immediately, thus we can see that in the notification, the translation does not work, because the state remains null. 截屏2020-11-20 下午3 59 02

    Reviewed by peter-jerry-ye at 2020-11-20 08:00
  • 2. Validation for CSV Files and Error Handling

    Currently there are no validators available for processing the CSV file, moreover if any of the data has errors leading to one of the row not being inserted, the dialog box just haults on loading.

    Reviewed by kunal-relan at 2020-05-11 12:51
  • 3. Minimize HTTP requests

    Hi, and thanks for sharing this module, it's really helpful.

    I have a concern with the way you send the imported records to the backend (using a list of create calls). This is not efficient from a network point of view, and can even be seen as a DoS from the backend.

    May I suggest you call a new verb on the dataProvider (something like createMany), and leave the implementation to the developers? That way, if their API allows a single call to create many records, they can optimize the call.

    I also have a concern about the create / update choice un the UI. In my opinion, the button should be smarter, and detect the records that are already present (using a getMany), then update those and create the remaining ones. The option in the dialog should disappear, and only be shown if necessary (just like the file copy dialog in your system asks for a confirmation to ignore, update, or stop in that case).

    Finally, I suggest that clicking on the "import" button opens the file selector right away. The file format (csv/tsv) can be enforced by a type attribute in the file input, and the presence of an id column can be checked a posteriori.

    These limitations are kind of blockers for me. Is there a chance that you implement these in your module, or should I redevelop another module on my side?

    Reviewed by fzaninotto at 2020-08-24 11:07
  • 4. Unsupported fetch action type createMany

    When I tried to use the import function, I am seeing:

    Unsupported fetch action type createMany
    

    could you please provide an example of how to implement createMany

    Reviewed by HarryF514 at 2021-07-19 20:25
  • 5. Post upload report handler

    The current api doesn't allow the client to deal with individual server-side failures since the Promise.all in the create/update functions are literally all or nothing.

    As a solution, I added the "reportCallback" option which allows all of the dataProvider calls to finish successfully so that the Dialog can survive a failure, and allows the client to specify how to handle the results (which include error information) in their own code.

    As an example, we handle reporting results like this...

    `import { downloadCSV } from 'react-admin'

    import { unparse } from 'papaparse/papaparse.min';

    const reportCallback = results => downloadCSV(unparse(results))

    ... < ImportButton reportCallback={reportCallback} / > ... `

    Reviewed by axehomeyg at 2020-08-05 06:17
  • 6. Some suggestions

    Hi @benwinding

    I just made some PRs in order to resolve some issues I had in the last weeks. I hope you'll like the improvements.

    I have some other suggestions that can be discussed here if you want.

    Suggestions

    https://github.com/benwinding/react-admin-import-csv/blob/84fdf2fd800c1471be73d05246febca0bfeb6f44/src/import-csv-button.tsx#L16

    I suppose that all console.log should be forbidden, right?

    https://github.com/benwinding/react-admin-import-csv/blob/84fdf2fd800c1471be73d05246febca0bfeb6f44/src/import-csv-button.tsx#L18

    I used to dispatch an action crudUpdateMany in order to save that in the redux store and then it will trigger an UPDATE_MANY event to the according data provider. You, otherwise, you use directly the data provider for each item. I am not sure what is the best use of this ATM. Otherwise, I can see there is 2 kind of operations:

    • UPDATE if the primary key does not exist in the list
    • CREATE if the primary key already exist

    It can be a little tricky but I believe we should allow the developer to choose the action to execute in those cases, don't you think? Like adding a new prop.

    https://github.com/benwinding/react-admin-import-csv/blob/84fdf2fd800c1471be73d05246febca0bfeb6f44/src/csv-extractor.ts#L14

    Also, we manage papaparse props ourselves. That could be interesting to set default props and then let the developers override the props based on their own needs. Maybe by reusing the same standard of react-admin. They use an exporter prop, we could have an importer prop.

    Reviewed by Odonno at 2020-03-30 07:58
  • 7. Unexpected behaviour on import with dynamicTyping

    Sorry to disturb you again, but I've found this:

    If you change your example posts.js to:

    const ListActions = (props) => {
      const {
        className,
        basePath,
        total,
        resource,
        currentSort,
        filterValues,
        exporter,
      } = props;
      const config = {
        logging: true,
        validateRow: async (row) => {
          if (row.id) {
            // throw new Error("AAAA");
          }
        },
      };
      return (
        <TopToolbar className={className}>
          <CreateButton basePath={basePath} />
          <ExportButton
            disabled={total === 0}
            resource={resource}
            sort={currentSort}
            filter={filterValues}
            exporter={exporter}
          />
          <ImportButton {...props} {...config} parseConfig={{dynamicTyping: true}}/>
        </TopToolbar>
      );
    };
    

    i.e. delete postCommitCallback and add this parseConfig, when you import data with conflicting ids, it will not show the dialog but quit directly. Is it an expected behaviour? Thanks.

    Reviewed by peter-jerry-ye at 2020-11-23 09:33
  • 8. array of one element

    I transform some data width transform func in parse config to array of numbers (for example [123,123,123,123]) If array has 2 and more elements, it transmitted to dataProvider normally But if array has one element (for example [123]), in transmitted to dataProvider as single number 123

    Reviewed by shrue348 at 2020-12-24 15:24
  • 9. Disable "Import As New"?

    I have an app where users should not be able to create new records - only update existing records.

    I tried to find a way to disable the Import As New feature using REFs but wasn't able to.

    Is there an easier way to do what I want to do here?

    Reviewed by copyaplexy at 2020-07-23 19:58
  • 10. CSV with empty column Error Handling

    When you try to import CSV with an empty column it throws an error in logging but there is no way to catch that error. I have tried to use papaparse skipEmpty lines option but that does not work either. Also tried validRow but I think the error is before executing that function. It would be great if you can help me out here. Thanks in advance. Screenshot 2022-04-22 at 1 36 40 PM

    Reviewed by Talhafayyaz11 at 2022-04-22 08:37
  • 11. The response to 'getMany' must be like { data : [...] }

    Hi there!

    I'm having this issue, I'm not sure if I'm missing something, but here I go:

    When I upload a CSV file, it looks like is making a get request filtering some data, and I'm dealing with this error:

    The response to 'getMany' must be like { data : [...] }, but the received data is not an array. The dataProvider is probably wrong for 'getMany'

    Screen Shot 2021-09-20 at 18 53 05

    This is happening even though I'm receiving the data in the correct way:

    Screen Shot 2021-09-20 at 18 55 32

    Here in Postman:

    Screen Shot 2021-09-20 at 18 58 00

    I hope someone can help me out with this, thanks a lot!

    Reviewed by koaladlt at 2021-09-20 21:58
  • 12. react-admin 4.x upgrade

    PLS dont merge there is an issue with react hooks

    everything else complies but I cant figure it out why I am getting

    Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons: 1. You might have mismatching versions of React and the renderer (such as React DOM) 2. You might be breaking the Rules of Hooks 3. You might have more than one copy of React in the same app See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.

    Reviewed by fkowal at 2022-04-26 13:42
  • 13. Error on GetIdsColliding when importing new rows

    Hello there,

    I'm wondering if that's the normal behaviour :

    • If I just export and re-import my CSV, I got my rows updated, that's well!
    • If I export a CSV, edit it, add 1 row with a non existant ID, I'm going to get an error on GetIdsColliding because the request return a 404 not found.

    I'm using API Platform for the data provider. Using your demo, it'll never happen because there's fake data (which not call any API)

    How can I handle that behaviour to don't throw any error and create the concerned row ? It is not automatic ? Should I use the callback from the ImportConfig in order to create it by myself ?

    I'm pretty sure that's not the goal or desired behaviour, I'm wrong ?

    Any help would be really appreciated, thanks in advance!

    image

    Reviewed by Loocos at 2022-03-24 10:03
  • 14. Not working with app cache

    If I use the application cache suggestion from https://marmelab.com/react-admin/Caching.html#application-cache , the import button do not work

    const cacheDataProviderProxy = (dataProvider, duration =  2 * 60 * 1000) =>
        new Proxy(dataProvider, {
            get: (target, name) => (resource, params) => {
                if (name === 'getOne' || name === 'getMany' || name === 'getList') {
                    return dataProvider[name](resource, params).then(response => {
                        const validUntil = new Date();
                        validUntil.setTime(validUntil.getTime() + duration);
                        response.validUntil = validUntil;
                        return response;
                    });
                }
                return dataProvider[name](resource, params);
            },
        });
    
    export default cacheDataProviderProxy(myDataProvider);
    

    With the cache active, the import button do not make an API call, it stops as the image bellow: image

    If I delete it and use the myDataProvider, it works fine.

    Is there a solution?

    Thanks!!

    Reviewed by diegocgaona at 2021-12-20 15:46
  • 15. Access existing data in transformRows or another callback?

    My data has an email address as an identifier, but it also gets assigned a unique ID in the database. Is there a way to check if an entry already exists (and ideally match them up) as I import?

    Reviewed by BruceWheaton at 2021-08-25 01:00
  • 16. Inject record data into transformRows callback

    Hi there!

    I have a Show component for a 'Parent' resource and I want to import a CSV which contains entries of a 'Child' resource. Now I need to reference the currently displayed Parent ID in a field for every imported Child. I understand that I can add static information in the transformRows callback but how can I possibly inject the currently displayed Parent record into the function?

    Thank you in advance for any solution or hint in the right direction!

    const configuration: ImportConfig = {
      // ...
      transformRows: (csvRows) => {
        let rows = [];
        csvRows.map((row) => {
          row.parent_id = parent.id; // this is where I need the injected data
          rows.push(row);
        });
        return rows;
      },
      // ...
    }
    
    export const ParentShow = (props) => (
        <Show {...props}>
            <TabbedShowLayout>
                <Tab label="Parent Details">
                    <TextField source="parentdetail1" />
                    <TextField source="parentdetail2" />
                </Tab>
                <Tab label="Children" path="children">
                    <ImportButton {...props} {...configuration} />
                    <ReferenceManyField reference="children" target="parent_id">
                        <Datagrid>
                            <TextField source="childdetail" />
                        </Datagrid>
                    </ReferenceManyField>
                </Tab>
            </TabbedShowLayout>
        </Show>
    );
    
    Reviewed by freddyseubertnio at 2021-07-09 14:44
An NFT Marketplace React dApp demo on the Polygon blockchain with basic functionalities such as acquire and mint NFTs using ERC721 Smart Contract and Interplanetary File System IPFS.
An NFT Marketplace React dApp demo on the Polygon blockchain with basic functionalities such as acquire and mint NFTs using ERC721 Smart Contract and Interplanetary File System IPFS.

Polygon-NFT-marketplace generated from jellydn/dapp-starter Opinionated Dapp Starter Template Homepage - Marketplace NFT Minting Features The React Fr

May 8, 2022
Simple File uploader using React , styled-components and firebase

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

Dec 30, 2021
A little plugin for Bootstrap 4 custom file input

bs-custom-file-input A little plugin which makes Bootstrap 4 custom file input dynamic with no dependencies. You can use it on React and Angular too b

May 12, 2022
Create-editorconfig - A simple npm script to generate an .editorconfig file

Create EditorConfig A simple npm script to generate an .editorconfig file. You d

Jan 11, 2022
Black Dashboard React is a beautiful Bootstrap 4, Reacstrap and React (create-react-app) Admin Dashboard with a huge number of components built to fit together and look amazing.
Black Dashboard React is a beautiful Bootstrap 4, Reacstrap and React (create-react-app) Admin Dashboard with a huge number of components built to fit together and look amazing.

Black Dashboard React Black Dashboard React is a beautiful Bootstrap 4, Reacstrap and React (create-react-app) Admin Dashboard with a huge number of c

May 8, 2022
Volt React is a free and open source admin dashboard template built in React.js and based on the latest version of the Bootstrap 5
Volt React is a free and open source admin dashboard template built in React.js and based on the latest version of the Bootstrap 5

Volt React is a free and open source admin dashboard template built in React.js and based on the latest version of the Bootstrap 5 CSS framework. It features over 100 UI elements, plugins, and example based built with React components.

May 12, 2022
Free React Admin Dashboard made with Material UI components and React.
Free React Admin Dashboard made with Material UI components and React.

Minimal UI (Free version) Free React Admin Dashboard made with Material UI components and React. Minimal Kit FREE Minimal Kit PRO 7 Demo Pages 40 demo

May 10, 2022
🔮 Sofia React Template - Admin Dashboard Template built with React
🔮 Sofia React Template - Admin Dashboard Template built with React

?? Sofia React Template - Admin Dashboard Template built with React

May 12, 2022
React Reduction - Free Admin Template Built with React and Bootstrap4
React Reduction - Free Admin Template Built with React and Bootstrap4

React Reduction - Free Admin Template Built with React and Bootstrap4 Preview You can check out live preview. Quick Start Clone the repo git clone htt

May 11, 2022
CoreUI React is a free React admin template based on Bootstrap 5

CoreUI Free React Admin Template v4 CoreUI is meant to be the UX game changer. Pure & transparent code is devoid of redundant components, so the app i

May 14, 2022
react redux for CMS/Enterprise class App/ERP/Admin with ant-design
react redux for CMS/Enterprise class App/ERP/Admin with ant-design

Feature List hot reloading/browser-sync/redux devtools on dev build minify/chunkhash/trackJS on production build eslint both of terminal and pre-commi

Apr 15, 2022
Free React.js Admin template
Free React.js Admin template

Azia Admin React is yet another incredible admin template from BootstrapDash that is based on the Bootstrap framework. The template is tastefully designed and coded to perfection. Azia Admin React comes packed with a lot of bootstrap components, UI elements, and built-in sample pages to kick-start your project.

May 4, 2022
🌊 A template for building an SaaS/admin application using React + Material-UI.
🌊  A template for building an SaaS/admin application using React + Material-UI.

?? Remains of a SaaS business I once tried to build. Now transformed into a template for building an SaaS/admin application using React + Material-UI.

May 6, 2022
Mosaic Lite is a free admin dashboard template built on top of Tailwind CSS and fully coded in React
Mosaic Lite is a free admin dashboard template built on top of Tailwind CSS and fully coded in React

Mosaic Lite is a responsive dashboard template built on top of TailwindCSS and fully coded in React

May 17, 2022
React Blur Admin - Styles And Components

React Blur Admin - Styles And Components

May 6, 2022
Berry is a creative free react admin template build using the Material-UI
Berry is a creative free react admin template build using the Material-UI

Berry is a creative free react admin template build using the Material-UI. It is meant to be the best User Experience with highly customizable feature-riched pages. It is a complete game-changer React Dashboard Template with easy and intuitive responsive design as on retina screens or laptops.

May 12, 2022
Berry free react material-ui admin template for easing and faster web development.
Berry free react material-ui admin template for easing and faster web development.

Berry is a creative free react admin template build using the Material-UI. It is meant to be the best User Experience with highly customizable feature-riched pages. It is a complete game-changer React Dashboard Template with easy and intuitive responsive design as on retina screens or laptops.

May 17, 2022
Sample React js admin theme

React js and Tailwind css Admin template This project was bootstrapped with Create React App. Demo Website Demo: https://react-js-admin.netlify.app/.

Jun 28, 2021