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