question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Bug: button onFocus not called when focus() called from useEffect cleanup

See original GitHub issue

React version: 16.12.0

Steps To Reproduce

  1. Have a <button/> HTML element with onFocus event handler defined
<button ref={btnRef} onFocus={() => console.log("I'm in focus")}>Focus Target</button>
  1. Have another component with useEffect cleanup code which will call focus()
React.useEffect(() => {
  const ref = btnRef.current;  // btnRef is a ref to the button component above
  return () => {
    console.log("effect cleanup ", new Date().toISOString());
    ref.focus();
  };
}, []);

When the cleanup code from useEffect is called, onFocus event handler of the button is not called. The button is rendered in a component which is mounted when useEffect cleanup executes. For example it can be in the parent component.

Link to code example: https://codesandbox.io/s/onfocus-not-called-from-useeffect-cleanup-npw7k

  1. Open example in browser (tested in Chrome 79.0.3945.88 and in Firefox 72.0.1).
  2. Open dev console
  3. Click on Focus target button. See another component below it with two buttons.
  4. Remove focus from Focus target button by setting it in adjacent input field
  5. Click on Set focus button. Observe that focus is set to Focus target button and in console see message ‘I’m in focus…’. This message is logged from onFocus event handler
  6. Remove focus from Focus target button.
  7. Click Close me button. Observe that focus is set to Focus target button but no message in console from onFocus event handler

The current behavior

onFocus is not called

The expected behavior

onFocus is called

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:8
  • Comments:7 (2 by maintainers)

github_iconTop GitHub Comments

4reactions
benoitgrelardcommented, Oct 9, 2020

Hey all,

I think I have stumbled on the same bug but also discovered another weird behaviour in relation to this. Both issues I’m seeing here (the event one already mentioned, + the new one) appear to have been fixed in v17. That said, I thought I’d report it anyway because of the second behavior I saw, as I don’t know if the react team is aware.

I have setup a sandbox to demonstrate the issue.

  • The black box has an onFocusCapture listener.
  • I have also setup a native focus listener (capture phase too) on the document.
  • Pressing the “Open blue box” button opens a blue box component below.
  • When pressing the “Close blue box” button, the blue box component gets unmounted, and we move focus to the input (in an effect on unmount).

latest stable react 16.13.1

https://codesandbox.io/s/react-unmount-focus-bug-0qn8q

Here’s what we can observe:

  • First of all, at first it looks like focus wasn’t moved to the input but instead remained on the button.
  • If we take a closer look at the events we can see:
    • When unmounting, the react onFocusCapture event was never called (same issue as discussed in this thread I think), we only see the native one.
    • But we also see that the focus WAS indeed moved to the input as we wanted, BUT react moved it back to the input for some reason (this is the other issue I have been mentioning).

image

react 17.0.0-rc.3

Here is the exact same sandbox, but with react updated to v17, you can see both issues seem to be resolved:

  • The input ended up focused.
  • The series of events is correct.

https://codesandbox.io/s/react-unmount-focus-bug-fixed-in-v17-t7zcw image


Let me know if that weird re-focusing behavior was known or not. Happy to see if fixed in 17 but wasn’t sure if it was accidental like @gaearon mentioned.

✌️

1reaction
vkurchatkincommented, Jan 23, 2020

It also seems that this is only broken when the effect is unmounting, but works fine when it is updating:

// Helper component to trigger a function when either k changes, or it is unmounted
function OnUnmount({ fn, k }) {
    React.useEffect(() => {
      return () => {
        fn();
      };
    }, [k])

  return null;
}

export default function App() {
  const inputRef = React.useRef();
  const [state, setState] = React.useState(0);

  function run() {
    setState(s => s + 1);
  }

  return (
    <div>
      <button onClick={run}>Run</button>
      <OnUnmount fn={() => {
        inputRef.current.focus();
      }} key={state}/> //
      <input ref={inputRef} onFocus={() => {
        console.log('onFocus');
      }}/>
    </div>
  );
}

In this example the input isn’t even focused at all, but if you change key={state} to k={state} it works fine, although there is seemingly no reason for such a discrepancy.

It goes without saying that adding setTimeout(0) fixes things.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Controlling Input Field Focus With React State and UseEffect
I want to be able to use the button to toggle focus on and off at my choosing. The only thing not working...
Read more >
useFocusEffect - React Navigation
The useFocusEffect allows you to run an effect on focus and clean it up when the screen becomes unfocused. It also handles cleanup...
Read more >
Understanding React's useEffect cleanup function
Learn React's useEffect cleanup function to prevent unwanted application behaviors like memory leaks by cleaning up effects.
Read more >
React v17.0 Release Candidate: No New Features
The solution is to capture any mutable values inside the effect: useEffect(() => { const instance = someRef.
Read more >
How To Handle DOM and Window Events with React
Then you added a button called Save directly before the closing ... As mentioned earlier, the event here is not the native browser...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found