Reputation: 21
Below is my simplified version of the problem I'm trying to solve.
Anyone know why my handleKeydown
method is not receiving the updated activeTab
state variable? I though that because I added activeTab
as a dependency, the callback would always receive the updated value. However, that does not seem to be the case.
The callback is called, as expected, every time a key is pressed; However, activeTab
is always null
, the initial value. Any help is appreciated!
(I've included both a code snippet as well as a jsfiddle link (because the snippet does not seem to be rendering for some reason. The jsfiddle at least renders lol).)
https://jsfiddle.net/Tom904/98veow5b/4/
const Tabs = () => {
const [activeTab, setActiveTab] = React.useState(null);
const handleKeydown = React.useCallback(event => {
console.log(activeTab);
// if a user hits the escape key while a tab is active...
if (event.key === 'Escape' && activeTab) {
setActiveTab(null);
}
}, [activeTab]);
React.useEffect(() => {
window.addEventListener('keydown', handleKeydown);
return () => {
window.removeEventListener('keydown', handleKeydown);
};
}, []);
return (
<div>
<button onClick={() => setActiveTab('1')}>
1
</button>
<button onClick={() => setActiveTab('2')}>
2
</button>
<button onClick={() => setActiveTab('3')}>
3
</button>
<div>
Active tab: {activeTab || 'None'}
</div>
</div>
)
}
ReactDOM.render(
<Tabs/>,
document.getElementById('root')
);
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<div id="root"></div>
Upvotes: 2
Views: 701
Reputation: 191976
The handleKeydown
function is updated on every change of activeTab
. However the useEffect
in which you bind the function to the keydown
event is only called on mount. So although the function is recreated, the original instance, with the initial value of activeTab
, is called by the event handler.
The useCallback
here is redundant, and you can also use functional updates form of useState
to avoid the activeTab
dependancy:
const Tabs = () => {
const [activeTab, setActiveTab] = React.useState(null);
React.useEffect(() => {
const handleKeydown = event => {
setActiveTab(activeTab => event.key === 'Escape' && activeTab ? null : activeTab);
};
window.addEventListener('keydown', handleKeydown);
return () => {
window.removeEventListener('keydown', handleKeydown);
};
}, []);
return (
<div>
<button onClick={() => setActiveTab('1')}>
1
</button>
<button onClick={() => setActiveTab('2')}>
2
</button>
<button onClick={() => setActiveTab('3')}>
3
</button>
<div>
Active tab: {activeTab || 'None'}
</div>
</div>
)
}
ReactDOM.render(
<Tabs/>,
document.getElementById('root')
);
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<div id="root"></div>
Upvotes: 2