If I make a couple of reasonable assumptions (for instance, that you have the closing ) on the useCallback call), your code for toggling selectAll works, though from your use of useCallback I suspect it doesn't quite work the way you want it to. Here's your code with those assumptions:
const {useState, useCallback} = React;
const TableContainer = ({children}) => {
return <div>{children}</div>;
};
const Table = ({children}) => {
return <div>{children}</div>;
};
const OTableHead = ({onSelectAllClick, children}) => {
console.log(`OTableHead is rendering`);
return <div>
<input type="button" onClick={onSelectAllClick} value="Select All" />
<div>{children}</div>
</div>;
};
const OTableBody = ({selectAll}) => {
return <div>selectAll = {String(selectAll)}</div>;
};
function OTable(props) {
const [selectAll, setSelectAll] = useState(false);
const onAllRowsSelected = useCallback(() => {
if (selectAll === false)
{
setSelectAll(selectAll => selectAll = true);
}
else
{
setSelectAll(selectAll => selectAll = false);
}
});
return (
<TableContainer>
<Table>
<OTableHead
onSelectAllClick={onAllRowsSelected}
setSelectAll={setSelectAll}
/>
<OTableBody
selectAll={selectAll}
/>
</Table>
</TableContainer>
);
}
ReactDOM.render(
<OTable />,
document.getElementById("root")
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>
But some things stand out:
You're not passing any dependency array to useCallback. That doesn't do anything useful, because useCallback will always return the new function you pass it. I suspect you meant to have an empty dependency array on it so that it always reused the first function (to avoid unnecessary re-rendering of OTableHead).
You're using the callback form of setSelectAll, but you're using hardcoded values (true and false). This code:
const onAllRowsSelected = useCallback(() => {
if (selectAll === false)
{
setSelectAll(selectAll => selectAll = true);
}
else
{
setSelectAll(selectAll => selectAll = false);
}
});
does exactly what this code would do (given that we know that selectAll is a boolean to start with, it would be very subtly different if we didn't know that):
const onAllRowsSelected = useCallback(() => {
setSelectAll(!selectAll);
});
because the if uses the version of selectAll that the function closes over, not the parameter the callback received. (setSelectAll(selectAll => selectAll = false); is functionally identical to setSelectAll(() => false), assigning to the parameter doesn't have any effect.) And in turn, that code is the same as this:
const onAllRowsSelected = () => {
setSelectAll(!selectAll);
};
But I suspect you used the callback version for the same reason you used useCallback.
The code doesn't succeed in avoiding having the re-rendering, as you can see from the console.log I added to OTableHead above.
useCallback is useful for avoiding making child elements re-render if the callback hasn't really changed, by memoizing the callback. Here's how you'd use it correctly in that code
Pass an empty dependencies array to useCallback so it only ever returns the first callback you define.
Use the parameter value that the function version of setSelectAll passes your callback.
Ensure that the component you want to have not re-render if the callback didn't change implements checks on its properties and doesn't re-render when they haven't changed. With a function component like OTableHead you can do that just by passing it through React.memo.
Here's the example above with those changes:
const {useState, useCallback} = React;
const TableContainer = ({children}) => {
return <div>{children}</div>;
};
const Table = ({children}) => {
return <div>{children}</div>;
};
// *** Use `React.memo`:
const OTableHead = React.memo(({onSelectAllClick, children}) => {
console.log(`OTableHead is rendering`);
return <div>
<input type="button" onClick={onSelectAllClick} value="Select All" />
<div>{children}</div>
</div>;
});
const OTableBody = ({selectAll}) => {
return <div>selectAll = {String(selectAll)}</div>;
};
function OTable(props) {
const [selectAll, setSelectAll] = useState(false);
const onAllRowsSelected = useCallback(() => {
// Callback version, using the parameter value
setSelectAll(selectAll => !selectAll);
}, []); // <=== Empty dependency array
return (
<TableContainer>
<Table>
<OTableHead
onSelectAllClick={onAllRowsSelected}
setSelectAll={setSelectAll}
/>
<OTableBody
selectAll={selectAll}
/>
</Table>
</TableContainer>
);
}
ReactDOM.render(
<OTable />,
document.getElementById("root")
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>
If you aren't worried about unnecessary re-rendering, then you can get rid of useCallback entirely and just do this:
const onAllRowsSelected = () => {
setSelectAll(!selectAll);
};