RDT mobile wrapper using React Context
Tags: react, design, ux, javascript
Published: 2022-08-07 00:01:50

One of the complex things a web developer needs to do is display tables, basically because a table needs to be easy to use, I mean provide pagination, filtering, sorting and everything else to handle the data. Sometimes achieving this on the desktop is complex but doable, but on the mobile side it might be even a bit more complex, that's why I'm now going to share my table wrapper to make this task easy.

As you read in the title, one thing we need to start building our tables is the [React Data Table Component (RDT)](https://react-data-table-component.netlify.app/?path=/story/getting-started-intro--page), which is a powerful package that provides a nice API for sorting, paging, filtering, styling, and more.

Now, if you took a look at the RDT documentation, you probably noticed that the setting to make the table mobile responsive is out of the box, so what is the point of this post?

Well, the option is there, but when you need to add action buttons to open a modal window, download a file, or whatever you need to do, there's a good chance you'll need to repeat the code multiple times, depending on how many tables your application needs.

In order to explain what will this wrapper resolve I’m going to provide a [repository](https://github.com/AgusRdz/rdt-context-demo) and a [codesandbox](https://codesandbox.io/s/rdt-context-demo-hnl6h0) with all the code used.

### RDT installation

- Install RDT by using one of the next commands:
    - `npm i react-data-table-component styled-components`
    - `yarn react-data-table-component styled-components`
- `data` prop: an array of objects where all table information is contained.
- `columns` prop: a memoized object where all [columns](https://react-data-table-component.netlify.app/?path=/docs/api-columns--page) props will be defined, e.g.:
```jsx
const columns = useMemo(() => [
  {
    name: 'Column name 1',
    id: 'columnId1',
    selector: ({ attribute1 }) => attribute1
  },
  {
    name: 'Column name 2',
    id: 'columnId2',
    selector: ({ attribute2 }) => attribute2
  },
  {
    name: 'actions',
    id: 'actions',
    cell: ({ attribute3 }) => (
      <div>
        <span onClick={(attribute3) => {}}Action 1</span>
      </div>
    ),
    hide: 'md'
  }
  // more columns...
], [])
```
By adding the property `hide: 'md'` if the window resolution is less than the desktop resolution the column will be automatically hidden, that is easy but now will needed a way to display on mobile and now is where the `ExpandedComponent` will be useful.

### Creating the wrapper
- `Table` component, basically will be a general wrapper created just to share the styles in case the app needs to use several tables, you can find more details here: [RDT Patterns](https://react-data-table-component.netlify.app/?path=/docs/getting-started-patterns--page#patterns)
- `ExpandedComponent` basic anatomy:
```jsx
<ExpandedWrapper>
  <Item label="ColumnName">{plainValue}</Item>
  <Item label="ColumnName"><span>children</span></Item>
</ExpandedWrapper>
```
- What are `ExpandedWrapper` and `Item`? Both are single components used to keep the styles consistency and you can create your own components as you want:
    - `ExpandedWrapper`
        ```jsx
        const ExpandedWrapper = ({ children }) => {
          return (
            <div className="grid text-sm mr-4">
              {children}
            </div>
          )
        }
        ```
    - `Item`
        ```jsx
        const Item = ({ label, children }) => {
          return (
            <div className="flex">
              <div className="max-w-max my-2 ml-16 font-semibold">
                <span>{label}</span>
              </div>
              <div className="max-w-max my-2 ml-4">
                <span>{children}</span>
              </div>
            </div>
          )
        }
        ```
        
### What the issue is?
The answer to this is quite simple, the `Datatable` component has a `data` prop and this is automatically shared in the expanded component, but if you need to provide functionality to your action buttons or links, you need to create the function for the desktop view in the “main component”  and the mobile function in the "expanded component", so this is where `React Context` will help to avoid code duplication using some single lines of code.

- `ExpandedComponentProvider`
```jsx
import { createContext } from 'react'
const ExpandedComponentContext = createContext()

const ExpandedComponentProvider = ({ children, ...rest }) => {
  return (
    <ExpandedComponentContext.Provider value={{ ...rest }}>
      {children}
    </ExpandedComponentContext.Provider>
  )
}

export { ExpandedComponentProvider, ExpandedComponentContext }
```

- `useExpandedComponent`
```jsx
import { useContext } from 'react'
import { ExpandedComponentContext } from 'contexts/ExpandedComponentProvider'

const useExpandedComponent = () => {
  const context = useContext(ExpandedComponentContext)

  if (context === undefined) {
    throw new Error(
      'useExpandedComponent must be used within a ExpandedComponentProvider'
    )
  }

  return context
}

export default useExpandedComponent
```

Now you can wrap your table using `ExpandedComponentProvider` to share all the functions or props you want and then in the expanded component use the hook `useExpandedComponent` to get them all and use them as you want them, note: `expandableRows` is a flag that you need to control when you want to use the expanded component, for example using a media query or a function to get the window width, e.g.:

```jsx
import { useCallback, useMemo } from 'react'
import { Table } from 'components/Table'
import { ExpandedComponentProvider } from 'contexts/ExpandedComponentProvider'
import ExpandedExampleComponent from 'components/ExpandedExampleComponent'

const Example = () => {
  const data = [
    {
      attribute1: 'attribute1'
    },
    {
      attribute2: 'attribute2'
    },
    {
      attribute3: 'attribute3'
    }
  ]

  const handleClick = useCallback(
    (url) => () => {
      window.open(url, '_blank', 'noopener,noreferrer,resizable')
    }, [])

  const columns = useMemo(() => [
    {
      name: 'Column name 1',
      id: 'columnId1',
      selector: ({ attribute1 }) => attribute1
    },
    {
      name: 'Column name 2',
      id: 'columnId2',
      selector: ({ attribute2 }) => attribute2
    },
    {
      name: 'Actions',
      id: 'actions',
      cell: ({ attribute3 }) => (
        <span onClick {handleClick(attribute3)}Action 1</span>
      ),
      hide: 'md'
    }
    // more columns...
  ], [])

  return (
    <ExpandedComponentProvider onClick={handleClick}>
      <Table
        name="demo"
        columns={columns}
        data={data || []}
        expandableRows
        expandableRowsComponent={ExpandedExampleComponent}
	// more props...
      />
    </ExpandedComponentProvider>
  )
}

export default Example
```

and the `ExpandedExampleComponent` :
```jsx
import { Item, ExpandedWrapper } from 'components/Table'
import useExpandedComponent from 'hooks/useExpandedComponent'

const ExpandedExampleComponent = ({ data }) => {
  const { onClick } = useExpandedComponent()
  const { attribute1, attribute2, attribute3 } = data

  return (
    <ExpandedWrapper>
      <Item label="Column Name 1">{attribute1}</Item>
      <Item label="Column Name 2">{attribute2}</Item>
      <Item label="Actions">
	<span onClick={onClick(attribute3)}Action 1</span>
      </Item>
    </ExpandedWrapper>
  )
}

export default ExpandedExampleComponent
```

#### Live preview:

![RDT Context Wrapper](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6ano9urqetqubdl4md9n.gif)

Repository: https://github.com/AgusRdz/rdt-context-demo

{% codesandbox qi02sr %}


### Final thoughts 
As you can see you can create amazing tables using RDT and in combination with React Context you can also add an easy way to handle them without much effort.

I hope that this small contribution can help you reduce your table implementation times, for me it has been very easy to work with them avoiding repeating code at the same time making their maintenance easier.

Happy Coding!