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