0

I am trying to use a hook to create a textbox that provides the value state as part of the hook.

However, I am not quite successful, as the textbox keeps losing focus whenever I tried to type.

This probably makes more sense if I show what I am doing.

Please see this Codepen for a quick demo. (https://codepen.io/david_yau/pen/RwWVoKz?editors=1111)

Detail explaination

// My first attempt
// App.jsx, the App
const App = () => {
  const [text, TextBox] = useTextBox();

  return (
    <div>
      <TextBox />
      <p>
        text: {text}
      </p>
    </div>
  )
}

// useTextBox.jsx
const useTextBox = () => {
  const [text, setText] = React.useState("");

  const onChange = (event) => {
    setText(event.target.value);
  }

  const TextBox = () => {
    return (
      <input type="text" value={text} onChange={onChange} />
    )
  };

  return [text, TextBox];
}

This way won't work because each time I typed in the textbox, the focus will be lost.

However, if I change the implementation to the following it will work.

The difference between this implementation is that the one above is rendering a new TextBox with <TextBox />. While the bottom one is using a rendered Textbox instead like {TextBox}.

// App.jsx
const App = () => {
  const [text, TextBox] = useTextBoxOne();

  return (
    <div>
      <h1> This one work </h1>
      {TextBox}
      <p>
        text: {text}
      </p>
    </div>
  )
}

// useTextBox.jsx
const useTextBox = () => {
  const [text, setText] = React.useState("");

  const onChange = (event) => {
    setText(event.target.value);
  }

  const TextBox = (
      <input type="text" value={text} onChange={onChange} />
    );

  return [text, TextBox];
}

If it helps at all, I got this idea from this course: https://frontendmasters.com/courses/complete-react-v5/, around the "Custom Hook" session.

Just to reiterate the question since this is quite a long post. I am wondering why does the first approach does not work because it keeps losing focus, while the second one does.

2
  • What is the question? Commented Apr 26, 2020 at 8:55
  • Sorry let me edit it. Commented Apr 26, 2020 at 9:01

1 Answer 1

2

Because const TextBox = () => <input /> will create a different React component every time useTextBoxTwo() is called, while const TextBox = <input /> is just a React element, rendered in the same Component (in App).

In the current implementation, when the element inside a component changes, ReactDOM checks whether it has the same element name (e.g. "input") and doesn't have a different key - when true, it re-uses the same DOM element, e.g. following 2 React-elements will reuse the same DOM-element (not losing focus):

{condition ? <input value="a" /> : <input value="b" />}

But 2 different React Components will trigger unmounting of the old one, removing it from DOM, and mounting of the new one, creating a new DOM element (=> losing focus).


So, don't create components inside custom hooks, create custom components that use hooks.

Sign up to request clarification or add additional context in comments.

5 Comments

Thank you so much for your answer. I think I kind of get the picture. But for useTextBoxTwo(), isn't the new <TextBox /> a <input /> at the end of the day? I tried digging around for relevant information but don't have any luck. Do you have an example, or some documentation that can demonstrated what happened? Thank you so much again.
check Difference between React Component and React Element.. or React Dev Tools in your browser
hi @Aprillion, thank you for your resources. I think I have a better understanding of it now. So in both useTexBoxOne and useTextBoxTwo, they are both returning a new "item", but one is a new element, and the other is a new component. And when rendering in React, a new element will not necessary trigger a recreate of a new DOM-object, thus keeping the focus. However, a new component will always trigger a unmount + mount action, thus losing focus. Am I correct?
As a side question, what is your opinion on returning element in hook?
not sure about the "always" part - you would have to check whether it's part of specification or just an implementation detail of ReactDOM (=> it could be different in React Native or Enzyme). Returning elements should be always fine, it's just a normal JS value like "hello" or {data} or <b>hello</b>

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.