greena13 / react-hotkeys

Declarative hotkey and focus area management for React
https://github.com/greena13/react-hotkeys
ISC License
2.15k stars 161 forks source link

Handler function doesn't reference the latest state #293

Closed stephensamra closed 3 years ago

stephensamra commented 3 years ago

Given the following...

const [value, setValue] = useState(0)

const keyMap = {
  increment: 'up',
  show: 'enter'
}

const handlers = {
  increment: () => setValue((v) => v + 1),
  show: () => console.log('show:', value)
}

return (
  <HotKeys keyMap={keyMap} handlers={handlers}>
    {/* */}
  </HotKeys>
)

... no matter how many times increment is called, show always prints 0.

You can see the issue happening in this CodeSandbox. If you press the up arrow 5 times and then press enter, the console prints:

the value is: 0
the value is: 1
the value is: 2
the value is: 3
the value is: 4
the value is: 5
show: 0

I would expect the last line to say show: 5.

Any help would be appreciated, thank you!

Harjot1Singh commented 3 years ago

I believe it is because handlers are not remountable/changed when the handlers object changes, by design. See https://github.com/greena13/react-hotkeys#setting-dynamic-hotkeys-at-runtime.

The reason that it increments correctly is because you're using the function form of setting a React state. If you did increment: () => setValue(value + 1), you'd find you'd have the same issue with incrementing as you do with the show function, since the updated functions are not passed to <Hotkeys> when changed.

The quickest way to fix this is add the allowChanges prop.

stephensamra commented 3 years ago

@Harjot1Singh Adding the allowChanges prop worked. Thank you!