Edit: I found the solution as a result of cbreezier's advice, as well as learning more about how the table hook was working (which I had found on YouTube and foolishly did not fully understand). Sending currentData back to ParentComponent via the props callback was what I took from their answer. However, passing currentData to the callback was not working from tableHooks. I then realized that while it was able to access the rows, it was not able to access the currentData. Changing Cell: ({ row }) to Cell: ({ currentData, row }) did the trick.
If you are trying to figure out a similar issue in your own React app, this may not be the exact answer, but I would suggest understanding cbreezier's answer and hopefully that sets you on your way.
Original Question:
I have been encountering an issue for a few days now that appears to be rooted in my inexperience with React and Javascript, but without knowing what the issue is, it has been difficult to search for a solution. Here is some example code that somewhat isolates the issue:
export const ParentComponent = () => {
const [currentData, setCurrentData] = useState([]);
const fetchCurrentData = async () => {
let fetchedCurrentData = await getFunctions.getCurrentData();
setCurrentData(fetchedCurrentData);
}
const handleButtonPress = (row) => {
console.log(currentData);
}
return (
<div>
<ChildComponent data={currentData} handleButtonPress={handleButtonPress} />
</div>
)
}
export default function ChildComponent(props) => {
const columns = useMemo(() => COLUMNS, []);
const data = useMemo(() => props.data, [props.data]);
const tableHooks = (hooks) => {
hooks.visibleColumns.push((columns) => [
...columns, {
id: "add",
Header: "Add",
Cell: ({ row }) => (
<button onClick={() => props.handleButtonPress()}>Add</button>
)
}
])
}
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow
} = useTable({
columns,
data
}, tableHooks);
return (
<div>
<table {...getTableProps()}>
<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, i) => {
prepareRow(row);
return (
<tr {...row.getRowProps()}>
{
row.cells.map(cell => {
return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>;
})
}
</tr>
);
})}
</tbody>
</table>
</div>
)
}
The flow is as so:
- Data is received
currentDatastate variable is updated usingsetCurrentDataParentComponentis re-renderedChildComponentis also rendered and receivescurrentDatavia props- Additionally,
ChildComponentreceives a functionhandleButtonPressvia props ChildComponentusesreact-table(v7.8.0) to display the data, and then via a hook is able to create an additional column in the table, with a button for each row- The button is set to call
handleButtonPressfrom props onClick - Button is pressed and
handleButtonPressis called
The above flow is working correctly. The issue occurs at the end of this flow, when the function handleButtonPress is called from ChildComponent, as console.log(currentData) logs an empty array instead of the actual state of currentData. I have verified that currentData is populated before re-render of ParentComponent, and that it successfully reaches ChildComponent.
Would greatly appreciate some help with this! If I had to guess where I'm going wrong, it's that something about passing the function handleButtonPress via props is potentially sending a version of the function before currentData is set, but like I said I'm quite inexperienced with React and that's just a guess!
Thanks for reading.