Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to use Hotkeys in functional components? #3508

Closed
ae-draft opened this issue Apr 29, 2019 · 6 comments · Fixed by #4532
Closed

How to use Hotkeys in functional components? #3508

ae-draft opened this issue Apr 29, 2019 · 6 comments · Fixed by #4532

Comments

@ae-draft
Copy link

export function App() {
	const [isDark, setIsDark] = React.useState(true);

	const switchTheme = () => {
		console.log(111);

		setIsDark(!isDark);
	};

	const getClass = () => {
		return classNames('menu', {
			'dark-theme': isDark,
			'light-theme': !isDark
		});
	};

	const renderHotkeys = () => {
		return (
			<Hotkeys>
				<Hotkey label="Switch Theme" combo="ctrl+shift+space" global={true} onKeyDown={switchTheme} />
			</Hotkeys>
		);
	};

	return (
		<div className={getClass()}>
			<Button className="switch-view-btn" icon={IconNames.MENU} onClick={switchView} intent={Intent.SUCCESS} />
			<UserBlock isFull={isFull} />
			<Menu className="a2-menu-ul" items={items} navigationItems={navItems} isFull={isFull} />
		</div>
	);
}

HotkeysTarget(App as any);

ReactDOM.render(<App />, document.getElementById('lmenu-holder'));

I have to cast App to any, because

Argument of type '() => Element' is not assignable to parameter of type 'IConstructor'.
Type '() => Element' provides no match for the signature 'new (...args: any[]): IHotkeysTargetComponent'

And in my console i see message

[Blueprint] @HotkeysTarget-decorated class should implement renderHotkeys.

@adidahiya
Copy link
Contributor

This is unsupported. As you can see in the documentation it is clearly expecting a class component. const renderHotkeys has no binding to the App symbol there. Use a class.

@ae-draft
Copy link
Author

ae-draft commented May 1, 2019

Are there plans to support this use case? React offers a new technology hooks, but they can not be used in classes.

@noah79
Copy link

noah79 commented Aug 4, 2019

Same, I would love to use blueprint hotkeys but I am now fully committed to using hooks. I was able to sort of hack around this by doing the following:

@HotkeysTarget
export class AssortmentSplit_ProductTable extends React.Component<MyProps & {store: AssortmentSplitTableStore}> {
  render() {
    const {props: {...props}} = this
    return <AssortmentSplit_ProductTable_fn {...props} />
  }

  renderHotkeys() {
    // I tried getting to this via our content provider but it breaks hotkeys.  
    const {props: {store: {toggleShowSelectedProduct}}} = this
    return <Hotkeys>
      <Hotkey label={'Show Details'} combo={'d'} global preventDefault onKeyDown={toggleShowSelectedProduct} />
    </Hotkeys>
  }
}

This is super hacky though as I now have to pass in my store as trying to get it via the context breaks renderHotkeys and I can't get to it via my normal hook as I have to be in a class component.

Hooks are awesome and I sincerely hope you guys provide a useHotkeys one in the not too distant future.

@jtiagodev
Copy link

Having the same issue. Have my entire project using React hooks and unable to use this...

@t3db0t
Copy link

t3db0t commented Sep 13, 2019

FWIW, +1 on supporting Hooks. I'm glad I found about this now with my first component (a Tree) and not after I'd already implemented a bunch of other ones. I can use classes even though it means I'll have a mixed codebase (blegh), so it's not the end of the world, but...

@thomassuckow
Copy link

thomassuckow commented Feb 12, 2020

Untested, unfortunately HotkeyEvents/HotkeyScope is not exported or we could probably try this.

import { useCallback, useEffect, useMemo, KeyboardEvent } from "react";

function useHotkeys(hotkeys) {
    const localHotkeysEvents = useMemo(() => new HotkeysEvents(HotkeyScope.LOCAL), []);
    const globalHotkeysEvents = useMemo(() => new HotkeysEvents(HotkeyScope.GLOBAL), []);
     
    useEffect(() => {
            document.addEventListener("keydown", globalHotkeysEvents.handleKeyDown);
            document.addEventListener("keyup", globalHotkeysEvents.handleKeyUp);
            () => {
                document.removeEventListener("keydown", globalHotkeysEvents.handleKeyDown);
                document.removeEventListener("keyup", globalHotkeysEvents.handleKeyUp);
    
                globalHotkeysEvents.clear();
                localHotkeysEvents.clear();
            }
    }, []);

    localHotkeysEvents.setHotkeys(hotkeys.props);
    globalHotkeysEvents.setHotkeys(hotkeys.props);

    const tabIndex = hotkeys.props.tabIndex === undefined ? 0 : hotkeys.props.tabIndex;

    const onKeyDown = useCallback((e: KeyboardEvent<HTMLElement>) => {
        localHotkeysEvents.handleKeyDown(e.nativeEvent as KeyboardEvent);
    }, [localHotkeysEvents]);

    const onKeyUp = useCallback((e: KeyboardEvent<HTMLElement>) => {
        localHotkeysEvents.handleKeyUp(e.nativeEvent as KeyboardEvent);
    }, [localHotkeysEvents]);
    return { tabIndex, onKeyDown, onKeyUp };
}

@adidahiya adidahiya self-assigned this Feb 10, 2021
@adidahiya adidahiya mentioned this issue Feb 18, 2021
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants