Skip to main content
Version: 3.xx.xx

useImport

useImport hook allows you to handle your CSV import logic easily. It uses papaparse under the hood to parse CSV files.

import { useImport } from "@pankod/refine-core";

const { handleChange } = useImport(options);

Usage​

Assume we have a CSV file of this contents:

dummy.csv
"title","categoryId"
"dummy title 1","3"
"dummy title 2","44"

This file should be parsed as:

[
{
title: "dummy title 1",
categoryId: "3",
},
{
title: "dummy title 2",
categoryId: "44",
}
]

With input[type=file]​

import React from "react";
import {
useImport,
} from "@pankod/refine-core";

export const PostList: React.FC = () => {
const [total, setTotal] = React.useState(0);
const [processed, setProcessed] = React.useState(0);

const { handleChange } = useImport<IPostFile>({
onFinish: (results) => {
window.alert(JSON.stringify(results));
},
onProgress: ({ totalAmount, processedAmount }) => {
setProcessed(processedAmount);
setTotal(totalAmount);
},
});


return (
<>
<input
type="file"
onChange={(event) => {
if (event.target.files) {
handleChange({
file: event.target.files[0],
});
}
}}
/>
<span>{`${processed}/${total}`}</span>
</>
);
};

interface IPostFile {
title: string;
categoryId: string;
}

tip

The useImport hook contains all the props that the HTML Input element needs (type, accept, onChange) so you can use directly inputProps in your HTML input elements like this

import React from "react";
import {
useImport,
} from "@pankod/refine-core";

export const PostList: React.FC = () => {
const { inputProps } = useImport();
return (
<input
{...inputProps}
/>
);
};

useImport usage

When user clicks the <input> element and selects a CSV file, useImport parses the content with papaparse, creates the resources one by one or as batches (depending on the configuration). Which endpoint to create the given resource is inferred from the current route.

Resources are added one by one (useCreate) or as batches (useCreateMany) if explicitly configured with batchSize option. By default, batchSize is 1.

caution

If batchSize is more than 1, createMany method should be implemented in DataProvider.
Refer to DataProvider documentation for further information about importing the default css. β†’

Handling Relational Data​

In some cases, you might want to change/process the data of CSV file after parsing. Example cases of this requirement: your data contains relational data and references to data in other places, your backend API requires your data to be sent in a specific format. You can further customize useImport to achieve this.

Assume this is the CSV file we want to create resources from:

dummy.csv
"title","content","status","categoryId","userId"
"dummy title 1","dummy content 1","rejected","3","8"
"dummy title 2","dummy content 2","draft","44","8"
"dummy title 3","cummy content 3","published","41","10"

Since user and category are relational fields, we shouldn't store them as objects. Instead, we should keep only their id fields in our exported files. And CSV format doesn't support JSON data, we stored category.id as categoryId and user.id as userId.

When creating these resources back, we should map it back to our backend API's required format. mapData option allows us to do this. Here is an example:

const importProps = useImport<IPostFile>({
mapData: (item) => {
return {
title: item.title,
content: item.content,
status: item.status,
category: {
id: item.categoryId,
},
user: {
id: item.userId,
},
};
},
});

interface IPostFile {
title: string;
status: string;
content: string;
categoryId: string;
userId: string;
}

Now, parsed data is mapped to conform our APIs requirements.

API Reference​

Properties​

KeyDescriptionTypeDefault
resourceNameDefault resource name this button imports to. Inferred from route by default.string
mapDataA mapping function that runs for every record. Mapped data will be included in the request payload.(value: any, index?: number, array?: any[], data?: any[][]): any;
papaparseOptionsCustom Papa Parse options.ParseConfig
batchSizeRequests batch size. If it is 1, all records are sent one by one. By default, it is Number.MAX_SAFE_INTEGER to send all records in one batch. If it is more than 1, createMany should be implemented on DataProvider.number
onFinishCalled with errors and successful responses when all requests are sent.(results: { succeeded: ImportSuccessResult<TVariables, TData>[]; errored: ImportErrorResult<TVariables>[]; }) => void
successNotificationSuccessful Mutation notificationSuccessErrorNotification"Successfully created resource"
errorNotificationUnsuccessful Mutation notificationSuccessErrorNotification"There was an error while creating resource (status code: statusCode)" or "Error when updating resource (status code: statusCode)"
metaDataMetadata query for dataProviderMetaDataQuery{}
dataProviderNameIf there is more than one dataProvider, you should use the dataProviderName that you will use.stringdefault

Return Values​

PropertyDescriptionType
inputPropsProps to that you can pass <input /> element props.UseImportInputPropsType
handleChangeProps to handle <input type="file"> element onChangefunction
isLoadingIt can be used to handle the loading status for the Import operationboolean
mutationResultResult of the mutation/mutations of creating imported resourcesUseMutationResult<
{ data: TData },
TError,
{ resource: string; values: TVariables; },
unknown>
) | UseMutationResult<
{ data: TData[]},
TError,
{ resource: string; values: TVariables[]; },
unknown>
)

Type Parameters​

PropertyDesriptionDefault
TItemInterface of parsed csv dataany
TDataResult type of the data query type that extends BaseRecordBaseRecord
TErrorCustom error object that extends HttpErrorHttpError
TVariablesValues for mutation functionany

Live StackBlitz Example​