Skip to content
Advertisement

How to re-render react-table in .js when data changes in another .js

I am new to react-table and trying to re-render a react-table when data supplied to it changes. Is there anyway to do so? I have tried using useEffect() with no avail.

CLARIFICATION: Usage.js and Updater.js needs to be kept seperated with their functions as shown. I am open to using another approach than global variable if it can help rerender the table when the data value changes.

table.js

import React, {useMemo} from 'react'
import {useTable} from 'react-table'

export const Table = ({ columns, data, noDataComponent, ...rest }) => {
  const tableColumns = useMemo(() => columns, [columns]);
  const { getTableBodyProps, getTableProps, headerGroups, prepareRow, rows } = useTable({ columns: tableColumns, data });

  if (!rows.length) {
    if (noDataComponent) return noDataComponent;
    return <>No data</>;
  }

React.useEffect(() => {}, [data]) //does not re-render table

  return (
    <table {...getTableProps()} {...rest}>
      <thead>
        {headerGroups.map((headerGroup) => (
          <tr {...headerGroup.getHeaderGroupProps()}>
            {headerGroup.headers.map((column) => (
              <th {...column.getHeaderProps()}>{column.render('Header')}</th>
            ))}
          </tr>
        ))}
      </thead>
      <tbody {...getTableBodyProps()}>
        {rows.map((row) => {
          prepareRow(row);
          return (
            <tr {...row.getRowProps()}>
              {row.cells.map((cell) => {
                const { className, style } = cell.column;
                return (
                  <td {...cell.getCellProps({ className, style })}>
                    {cell.render('Cell')}
                  </td>
                );
              })}
            </tr>
          );
        })}
      </tbody>
    </table>
  );
};

globalVariable.js

module.exports = global.config = {
value: { exactval: [] }
}

Usage.js

data is updated by using a GET request in Updater.js and supplied using global variable in globalVariable.js.

import {Table} from './table.js'
...

<Table data={global.config.value.exactval} columns={COLS} />

Updater.js

import './globalVariable.js'

...

function DummyCall() { //Works fine 
    axios.get(URL, headers)
    .then(reponse => {
        global.config.value.exactval.push(reponse.ID[1])
        }
    ).catch(error => {
        console.log(error)
    }) }

...

<button type ="button" onClick={() => DummyCall()} > Test me! </button>

A simple explaination with example code would be greatly appreciated.

EDIT #1: Added how data is updated

EDIT #2: After taking the advice of @Obsidianlab and @Johnny, I used Context API using Johnny’s answer (there is an important bug in answer where needs to wrap the .Provider in return(), I am getting the following strange behaviour:

Updater.js

Updated based on @Johnny’s steps

import './globalVariable.js'

...

function DummyCall() { //Works fine 
    const updatedData = { ...context.data };

    axios.get(URL, headers)
        .then(reponse => {
            global.config.value.exactval.push(reponse.ID[1])
            updatedData.data.push(reponse.ID[1]);

        }
    context.setData(updatedData)
    ).catch(error => {
        console.log(error)
}) }

...

<button type ="button" onClick={() => DummyCall()} > Test me! </button>

For Usage.js

The following code below works and re-renders:

const context = useContext(TableDataContext) //needs to be there
<Table data={global.config.value.exactval} columns={COLS} />

The following code below does NOT re-render, however console says that the context.data.exactval value is updated.

const context = useContext(TableDataContext);
...
<Table data={context.data.exactval} columns={COLS} />

How can i fix non-render issue related to Context API?

Advertisement

Answer

So, from my understanding, you are updating a global variable created in a js file ./globalVariable.js. I haven’t seen the file, but I believe these variables are outside the scope of what react can “keep an eye on”.

So I suggest you to create a state for your data in the Usage.js, and then update it. Example:

Usage.js

import { Table } from './table.js';
...
const [data, setData] = useState(initialData); //define your inital data like your global variables

async function DummyCall() {
  try {
    const response = await axios.get(URL, headers);
    if(response.status === 200) {
       const updatedData = { ...data };
       updatedData.config.value.exactval.push(reponse.ID[1]);
       setData(updatedData)
    }

  } catch(error) {
    console.log(error);
  }
}
...
<button type ="button" onClick={() => DummyCall()} > Test me! </button>

Edit #1

Since you cannot merge both files or export the function, i suggest you tu use ContextAPI

TableDataContext.js

import { createContext } from 'react'

const TableDataContext = createContext();

const TableDataProvider = ({ children }) => {
  const [data, setData] = useState(initialData); //define your inital 

  <TableDataContext.Provider value={{ data, setData }}>
    {children}
  </TableDataContext.Provider>
}

export { TableDataContext, TableDataProvider };

Now you have to wrap your components taht need to consume the TableDataContext with the provider

If you want in your whole app, for example:

import { TableDataProvider } from 'path/to/context/TableDataContext'

<TableDataProvider>
  <App/>
</TableDataProvider>

This way, you can setData in DummyCall by importing from context:

Updater.js

import { useContext } from 'react';
import { TableDataContext } from 'path/to/context/TableDataContext'
....
const context = useContext(TableDataContext);

async function DummyCall() { 
   //...code
   // on setData
   const updatedData = { ...context.data };
   updatedData.config.value.exactval.push(reponse.ID[1]);
   context.setData(updatedData)
}

Finally just import the data from context in your table.js, and you will not need to pass by props

You can learn more about the ContextAPI on React Docs

Hope it works

Advertisement