Reputation: 192
I'm building a dropdown with suggestions that fetch data from an API. The input from the search bar is being stored using setState and it is updated when i change the value in the text input.
The thing is that I'm not managing to update the users lists from the dropdown each time I enter a new character in the text input. Can I somehow force the component to be rendered every time the props change? Or is there a better approach?
import React, {useState, useEffect} from 'react';
import Dropdown from '../Dropdown/Dropdown';
import './SearchBar.css';
// Component created with arrow function making use of hooks
const SearchBar = (props) => {
const [input, setInput] = useState('');
const [dropdownComponent, updateDropdown] = useState(<Dropdown input={input}/>)
useEffect(() => {updateDropdown(<Dropdown input={input}/>)}, [input])
const onChange = (e) => {
setInput(e.currentTarget.value)
updateDropdown(<Dropdown input={input}/>)
console.log("=(")
}
return(
<div className="search-bar">
<input type="text" placeholder={props.placeholder} onChange={onChange}/>
{dropdownComponent}
</div>
)
}
export default SearchBar;
Upvotes: 1
Views: 23529
Reputation: 1074148
I can't make the problem happen using your code in a simple test, but your onChange
does have a problem: It's using input
to update the dropdown, but it's not using useCallback
to ensure that input
isn't stale when it does. Either:
Don't update the dropdown in your onChange
, allowing your useEffect
callback to do it; or
Use e.target.value
instead of input
and get rid of the useEffect
updating the dropdown; or
Don't memoize the dropdown (e.g., don't put it in state) since you want to update it when the input changes anyway, just render it directly in the JSX
Of those, with what you've shown, #3 is probably the simplest:
const SearchBar = (props) => {
const [input, setInput] = useState('');
const onChange = (e) => {
setInput(e.currentTarget.value);
};
return(
<div className="search-bar">
<input type="text" placeholder={props.placeholder} onChange={onChange}/>
<Dropdown input={input}/>
</div>
);
}
Live Example:
const {useState, useEffect} = React;
function Dropdown({input}) {
return <div>Dropdown for "{input}"</div>;
}
const SearchBar = (props) => {
const [input, setInput] = useState('');
const onChange = (e) => {
setInput(e.currentTarget.value);
};
return(
<div className="search-bar">
<input type="text" placeholder={props.placeholder} onChange={onChange}/>
<Dropdown input={input}/>
</div>
);
}
ReactDOM.render(<SearchBar />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.2/umd/react-dom.production.min.js"></script>
Upvotes: 4