React Select更新其他状态(如remarks)时总是重渲染,如何解决?
Great question! This is a super common React performance hiccup—when your parent component re-renders (thanks to the remarks state updating as you type), all child components (including your React Select) re-render too, even if their props haven’t changed. Let’s break down how to fix this step by step.
Why This Happens
Every time you type in the remarks textarea, the remarks state updates, triggering a full re-render of the parent component. By default, React re-renders all child components during a parent re-render, no matter if their props are the same or not. The problem here is that your Select component is receiving props (like the onChange function or options array) that get recreated on every render, so React sees them as "new" and re-renders the Select unnecessarily.
Solution 1: Use React.memo with Cached Props
We can memoize the React Select component using React.memo so it only re-renders when its actual props change. To make this work, we need to ensure the props passed to Select have stable references between renders.
Here’s how to tweak your code:
- Cache the
onChangefunction withuseCallback: This keeps the function reference consistent unless its dependencies change. - Cache the
optionsarray withuseMemo: Prevents creating a new array reference on every render if the options don’t change. - Wrap the Select component with
React.memo: Tells React to skip re-rendering the Select if props are identical to the last render.
const [expense_currency, setExpenseCurrency] = React.useState(expense_claim.expense_currency); const [remarks, setRemarks] = React.useState(expense_claim.remarks); // Cache the onChange function - setExpenseCurrency is stable, so no extra re-renders here const handleCurrencyChange = React.useCallback(selected => { setExpenseCurrency(selected.value); }, [setExpenseCurrency]); // Cache options array - add select_currency to dependencies if it's dynamic (e.g., from props) const memoizedCurrencyOptions = React.useMemo(() => select_currency, []); // Create a memoized version of the Select component const MemoizedSelect = React.memo(Select); // ... return ( // ... <div className="form-group row p-0 col-sm-12 col-md-6"> <label className="text-nowrap col-form-label col-sm-4">Currency</label> <div className="col-sm-8 p-0" title={expense_currency}> <MemoizedSelect className="select2" placeholder="Select Currency" isDisabled={false} isClearable={true} value={expense_currency} options={memoizedCurrencyOptions} onChange={handleCurrencyChange} /> </div> </div> {/* Remarks textarea stays the same */} )
Solution 2: Extract the Select into a Separate Memoized Component
Another clean approach is to move the React Select into its own standalone component wrapped with React.memo. This isolates the Select from the parent’s re-renders unless its specific props change.
// Separate memoized component for currency selection const CurrencySelect = React.memo(({ value, onChange, options }) => { return ( <Select className="select2" placeholder="Select Currency" isDisabled={false} isClearable={true} value={value} options={options} onChange={onChange} /> ); }); // In your parent component: const [expense_currency, setExpenseCurrency] = React.useState(expense_claim.expense_currency); const [remarks, setRemarks] = React.useState(expense_claim.remarks); const handleCurrencyChange = React.useCallback(selected => { setExpenseCurrency(selected.value); }, [setExpenseCurrency]); const memoizedCurrencyOptions = React.useMemo(() => select_currency, []); // ... return ( // ... <div className="form-group row p-0 col-sm-12 col-md-6"> <label className="text-nowrap col-form-label col-sm-4">Currency</label> <div className="col-sm-8 p-0" title={expense_currency}> <CurrencySelect value={expense_currency} onChange={handleCurrencyChange} options={memoizedCurrencyOptions} /> </div> </div> )
Quick Notes
- If
select_currencyis dynamic (e.g., comes from parent props or state), add it to theuseMemodependency array so options update when needed. React.memodoes a shallow comparison of props. If you’re passing complex objects as props, you can add a custom comparison function as the second argument toReact.memo, but that’s rarely necessary here.setExpenseCurrencyis guaranteed to be stable across renders by React, so it’s safe to include in theuseCallbackdependencies without causing extra re-renders.
内容的提问来源于stack exchange,提问作者Rio A.P

