React passing down hooks causes rerender and loss of focus on input

Viewed 367

I've got a parent component in which I initialize some piece of state, which I then pass down to the children components so that they can update that. However, when the update is triggered, the component tree is re-rendered and my inputs lose focus. Adding a key did not help.

// App.tsx

export function App(props) {
  const useVal = useState("");

  return (
    <Router>
      <Switch>
        <Route
          exact
          path="/"
          component={() => (
            <StartScreen
              useVal={useVal}
            />
          )}
        />
        // ...
    </Router>
  );
}
// StartScreen.tsx

interface StartScreenProps {
  useVal: [string, React.Dispatch<React.SetStateAction<string>>];
}

function bindState<T>(
  [value, setState]: [T, React.Dispatch<React.SetStateAction<T>>]
) {
  return {
    value,
    onChange: ({ value }: { value: T }) => setState(value)
  }
}

export const StartScreen = (props: StartScreenProps) => {
  return (
    <form>
      <InputField
        key="myInput"
        {...bindState(props.useVal)}
      />
    </form>
  );
}

So, now when I start typing on my InputField (which is basically a wrapper on an <input>) on StartScreen.tsx, the input constantly loses focus as the component is totally re-rendered (I can see it in the DOM).

1 Answers

This happens because you are passing a function to the Route's component prop (I assume you are using react-router-dom) :

From the docs :

If you provide an inline function to the component prop, you would create a new component every render. This results in the existing component unmounting and the new component mounting instead of just updating the existing component.


To solve this problem use the render prop :

    <Route
      exact
      path="/"
      render={() => (
        <StartScreen
          useVal={useVal}
        />
      )}
    />

This allows for convenient inline rendering and wrapping without the undesired remounting explained above.

Related