This happens because the useState
hook is not "hooked" to your SearchComponent
, but your MyInput
component. Whenever, you call updateCitySearch()
you change the state of MyInput
, thus forcing the entire component to re-render.
SearchComponent
, is explicitly defined inside MyInput
. When citySearch-state
is updated, SearchComponent
loses focus because the initial virual DOM
surrounding it is no longer intact, instead you have a completely new piece of DOM. Essentially, you are creating a brand new SearchComponent
each time the MyInput
is updated by state.
Consider the following example:
function App() {
const [citySearch, updateCitySearch] = useState("");
console.log("rendered App again"); //always prints
const SearchComponent = () => {
console.log("rendered Search"); //always prints
const searchCityClick = () => {
alert(citySearch);
};
return (
<div>
<input
value={citySearch}
onChange={e => {
updateCitySearch(e.target.value);
}}
/>
<button onClick={searchCityClick}>Search</button>
</div>
);
};
return (
<div>
<div>
<SearchComponent />
</div>
</div>
);
}
Every time you update state, you would trigger both console.log()
, the App component re-renders and SearchComponent
gets re-created. A new iteration of myInput
is rendered each time and a new SearchComponent
gets created.
But if you were to define useState
inside SearchComponent
, then only SearchComponent
will re-render whens state changes, thus leaving the original myInput
component unchanged, and the current SearchComponent
intact.
function App() {
console.log("rendered App again"); //would never print a 2nd time.
const SearchComponent = () => {
const [citySearch, updateCitySearch] = useState("");
console.log("rendered Search"); //would print with new state change
const searchCityClick = () => {
alert(citySearch);
};
return (
<div>
<input
value={citySearch}
onChange={e => {
updateCitySearch(e.target.value);
}}
/>
<button onClick={searchCityClick}>Search</button>
</div>
);
};
return (
<div>
<div>
<SearchComponent />
</div>
</div>
);
}