---
myst:
html_meta:
"description": "Volto add-ons development training module 3, add-ons block view"
"property=og:description": "Volto add-ons development training module 3"
"property=og:title": "Volto add-ons development block view"
"keywords": "Volto"
---
# Improve the block view
Let's add CSV file parsing.
There are many CSV parsers available for NodeJS.
We'll use [Papaparse](https://www.npmjs.com/package/papaparse) because it also works in the browser.
We'll need to add the dependency to the add-on if you haven't already done so,
as instructed in the first chapter. When using yarn workspaces, the
workflow is a bit different. For our simple use case, we could probably run
`yarn add papaparse` inside the `src/addons/volto-datatable-tutorial`, but
the correct way is to run this command within the project root.
First, run `yarn workspaces info` to see the workspaces we have available.
```console
yarn workspaces info
{
"@plone-collective/volto-datatable-tutorial": {
"location": "src/addons/volto-datatable-tutorial",
"workspaceDependencies": [],
"mismatchedWorkspaceDependencies": []
}
}
```
To add a dependency to the package, run:
```sh
> yarn workspace @plone-collective/volto-datatable-tutorial add papaparse
```
And finally, the new block code within `src/DataTable/DataTable.jsx`:
```jsx
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Table } from 'semantic-ui-react';
import csv from 'papaparse';
import { getRawContent } from '@plone-collective/volto-datatable-tutorial/actions';
const DataTableView = ({ data: { file_path } }) => {
const id = file_path?.[0]?.['@id'];
const path = id ? `${id}/@@download` : null;
const dispatch = useDispatch();
const request = useSelector((state) => state.rawdata?.[path]);
const content = request?.data;
React.useEffect(() => {
if (path && !content) dispatch(getRawContent(path));
}, [dispatch, path, content]);
const file_data = React.useMemo(() => {
if (content) {
const res = csv.parse(content, { header: true });
return res;
}
}, [content]);
const fields = file_data?.meta?.fields || [];
return file_data ? (
{fields.map((f) => (
{f}
))}
{file_data.data.map((o, i) => (
{fields.map((f) => (
{o[f]}
))}
))}
) : (
No data
);
};
export default DataTableView;
```
Writing components where the `useEffect` triggers network calls can be pretty tricky.
According to [Built-in React Hooks](https://legacy.reactjs.org/docs/hooks-rules.html), hooks can't be triggered conditionally.
They always have to be executed.
For this reason, it's important to add relevant conditions inside the hook code.
Be sure to identify and prepare a way to tell, from inside the hook, if the network-fetching action should be dispatched.
## The React HOC Pattern
It is a good idea to split the code into generic "code blocks" so that
behavior and look are separated.
This has many benefits:
- It makes components easier to write and test.
- It separates business logic into reusable behaviors.
Can we abstract the data-grabbing logic?
Let's write a simple Higher-Order Component (HOC) that does the data grabbing.
The simplest HOC wrapper looks like this:
```jsx
const withFileData = (WrappedComponent) => {
return (props) => ;
};
export default withFileData(DataTableView);
```
Notice the similarity with Python decorators.
In our case, the HOC is a function that, given a component as input, returns
a React component.
Now let's move the file download and parsing logic to this HOC.
We'll create the `src/hocs/withFileData.js` file:
```jsx
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import csv from 'papaparse';
import { getRawContent } from '@plone-collective/volto-datatable-tutorial/actions';
const withFileData = (WrappedComponent) => {
return (props) => {
const {
data: { file_path },
} = props;
const id = file_path?.[0]?.['@id'];
const path = id ? `${id}/@@download` : null;
const dispatch = useDispatch();
const request = useSelector((state) => state.rawdata?.[path]);
const content = request?.data;
React.useEffect(() => {
if (path && !request?.loading && !request?.loaded && !content)
dispatch(getRawContent(path));
}, [dispatch, path, content, request?.loaded, request?.loading]);
const file_data = React.useMemo(() => {
if (content) {
const res = csv.parse(content, { header: true });
return res;
}
}, [content]);
return ;
};
};
export default withFileData;
```
This HOC now gets the data from the `Redux` store using the logic and code we've
previously used.
It then injects it as a new property onto the original wrapped component.
A HOC is a simple function that gets a component and returns another
component. For a Python developer, decorators are a very similar concept.
Take note that React component names must be referenced as `PascalCase` in JSX code.
Now the view component is simple, neat, and focused.
Now write the `src/hocs/index.js` file where you export the new HOC.
```jsx
import withFileData from './withFileData';
export { withFileData };
```
Back to the `src/DataTable/DataTable.jsx`, it becomes:
```jsx
import React from 'react';
import { Table } from 'semantic-ui-react';
import { withFileData } from '@plone-collective/volto-datatable-tutorial/hocs';
const DataTableView = ({ file_data }) => {
const fields = file_data?.meta?.fields || [];
return file_data ? (
{fields.map((f) => (
{f}
))}
{file_data.data.map((o, i) => (
{fields.map((f) => (
{o[f]}
))}
))}
) : (
No data
);
};
export default withFileData(DataTableView);
```