I have a HTML select drop down list (SortBy component), after selecting an option, the component rerenders, this is not wanted, I want to see the option I selected but it keeps reseting (rerendering) to the first option. I’ve tried useCallback with no luck
When A-Z or Price: Low to high is selected, I want that value retained in the HTML select box but it defaults back to Relevance
function App() {
const [items, setItems] = useState(products);
console.log(products);
const onChange = useCallback((e) => {
const { value } = e.target;
let arr = items;
switch (value) {
case ("relevance", "saleFirst"):
arr = saleItemsFirst(items, false);
break;
case "ascend":
arr = sortByName(items);
break;
case "price":
arr = sortByPrice(items);
break;
default:
arr = saleItemsFirst(items);
break;
}
setItems([...arr]);
},
[items]
);
const SortBy = useCallback(() => {
return (
<select name="filters" onChange={onChange}>
<option value="relevance">Relevance</option>
<option value="ascend">A-Z</option>
<option value="priceLow">Price: Low to high</option>
<option value="saleFirst">Sales items first</option>
</select>
);
}, [onChange]);
return (
<div className="App">
<SortBy />
<hr />
<section>
{items.map((val) => (
<div>
<span>{val.productName}</span> <div>${val.price}</div>
<hr />
</div>
))}
</section>
......
2
Answers
The Problem:
SortBy
is getting re-defined wheneveritems
changes becauseitems
is in the dependency array for your useCallback, which tells it when to re-run, re-defining the returned function reference. When that happens, it’s a whole new component instance, and it’s being remounted with initial values.items
has to be in your dependency array if you define a function this way, otherwise, whenitems
changes, your memoized (useCallback’d) component will be stale.The Solution:
Rather than declaring
SortBy
inside of the App component, you’ll need to declare it outside, and pass in any dependencies it needs as props. That way, the SortBy component will be a module-level constant, and it will only mount once when App mounts.Takeaway:
It’s generally a bad practice to define components inside of other components. Avoid it. The only possible exception are very dimple (like, one-liner) components that have no state concerns. Even then I’d advise defining them outside other components.
Instead of keeping the sorted list items in state you could just keep track of which sort option is selected and memoize the sorted item list, updating it when the selected sort changes: