Search code examples
javascriptreactjsstate

re-render input component not losing focus when re-rendering


I present two different ways to render an input component;

E.g.1

function myComponent(){                 
const [st, setSt] = useState('');       
                                        
return <div>                            
{Math.random()}                         
<input value={st}                      
  onChange={e=>setSt(e.target.value)/>  
</div> 

E.g. 2

 function StateHelper() {
  const [st, setSt] = useState("");

  return { context: { st, setSt } };
}

function MyHelper(prop) {
  const ActualComponent = prop.capture;

  return <ActualComponent />;
}

function MyComponent() {
  const { context } = StateHelper();

  const capturedComponent = () => (
    <input
      value={context.st}
      onChange={(e) => {
        context.setSt(e.target.value);
      }}
    />
  );

  return <MyHelper capture={capturedComponent} />;
}

Runnable Fiddle For e.g. 2

I seem to be confused on one of the fundamentals.

On e.g. 1, when I type, I don't lose focus on the input component.
On e.g. 2, on each letter typed, I lose focus.

On both of these examples, Math.random() changes, so they both re-render.

I don't know what's the difference, and why I keep losing focus on e.g. 2 ?

I'm trying to follow e.g. 2 as it helps me manage my code better, but can't seem to understand it. What am I not understanding about react component re-rendering and how can I fix it?


Solution

  • Your const capturedComponent = () => ( is an inner function whose identity changes with each invocation of the component declaring it.

    That makes React think that the component is not the same one that it rendered the last go, so it will unmount the old one and remount the new one (even if they might be identical in output), which in turn causes the focus loss on the input element.

    See the documentation for the React ESlint rule for this issue for more details.