Felipe Souza
Felipe Souza

Reputation: 192

Update a component with onChange. React-Hooks

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

Answers (1)

T.J. Crowder
T.J. Crowder

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:

  1. Don't update the dropdown in your onChange, allowing your useEffect callback to do it; or

  2. Use e.target.value instead of input and get rid of the useEffect updating the dropdown; or

  3. 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

Related Questions