biscuitstack
biscuitstack

Reputation: 12122

useCallback does not inherit parent props

I'm detecting keypresses in a component that is aware of what dialog component is currently open elsewhere in the app via a prop, currentDialog. Normally I'd be able to access this prop in a nested function but it seems this isn't possible when using useCallback:

export const AllAreaNav = (props) => {

    console.log('AllAreaNav / props.currentDialog: ', props.currentDialog); // displays correct dialog

    const handleKeyPress = useCallback((event) => {

        console.log('AllAreaNav / handleKeyPress / props.currentDialog: ', props.currentDialog); // displays undefined
        
        if(event.keyCode === 70) {
            //Do whatever when F is pressed
            console.log("F key pressed");
            if (props.currentDialog == "DialogSearchBar") {
                // Take action based on DialogSearchBar being active
            } else {
                // Take action based on DialogSearchBar NOT being active
            }

        }

    }, []);

    useEffect(() => {
        // Listener for keypresses
        document.addEventListener("keydown", handleKeyPress, false);
        return () => {
            document.removeEventListener("keydown", handleKeyPress, false);
        };
    }, []);

    return (
        {jsxElements}
    )
};

So now I'm a little unsure of a straightforward way of passing this as a parameter - assuming this would be the next step. From researching I believe it's fine to add another parameter alongside event? That this should work as I intend:

const handleKeyPress = useCallback((event, currentDialog) => {

However, I'm not entirely sure of how to initially pass this to the function. If I modify the listener to be:

document.addEventListener("keydown", handleKeyPress(event, props.currentDialog, false);

I'm unsure if this is correct, or where exactly to define event in this context, in the manner handleKeyPress defaults its as a parameter.

Upvotes: 2

Views: 58

Answers (1)

Rostyslav
Rostyslav

Reputation: 2866

It seems that you were trying to resolve the problem by parametrizing the callback but you did not have an event in your context. In order to parametrize AND have the event in context, you must create a closure on the currentDialog parameter.

You can try this solution:

/**
 * move callback definition outside the component
 * and create a closure on currentDialog (function returning a function)
 */
const handleKeyPress = (currentDialog) => (event) => {
  if (event.keyCode === 70) {
    //Do whatever when F is pressed
    console.log("F key pressed");
    if (currentDialog == "DialogSearchBar") {
      // Take action based on DialogSearchBar being active
    } else {
      // Take action based on DialogSearchBar NOT being active
    }
  }
};

export const AllAreaNav = (props) => {
  console.log("AllAreaNav / props.currentDialog: ", props.currentDialog); // displays correct dialog

  useEffect(() => {
    // Listener for keypresses
    const listener = handleKeyPress(props.currentDialog); // use closure to create a callback closured on currentDialog value
    document.addEventListener(
      "keydown",
      listener,
      false
    );
    return () => {
      document.removeEventListener(
        "keydown",
        listener,
        false
      );
    };
  }, [handleKeyPress, props.currentDialog]); // pass callback and currentDialog value to dependency array

  return { jsxElements };
};

Upvotes: 2

Related Questions