React函数组件中如何避免列表输入框因重渲染失去焦点
解决React列表项输入框修改时失去焦点的问题
问题描述
我有一个需要渲染的列表项集合(openItems),每个列表项都配有输入框,用于修改对应项的duration属性。但每次修改属性时,父组件(openItems)及其所有子组件(包括输入框)都会重渲染,导致输入框失去焦点。尝试过使用React.memo,但由于数据确实发生了变化,并未起到预期效果,希望了解React函数组件中正确的实现方案。
当前简化代码(Antd组件)
const Page = () => { const dispatch = useDispatch(); const openItems = ...get items from state const setMs = (id: string, ms: number) => { dispatch(changeDuration({ id, duration: ms })); } const InputMs: React.FC<{ row: RowInterface }> = (props) => ( <InputNumber defaultValue={props.row.durationMs} onChange={(v) => setMs(props.row.id, v)} /> ) const RowContent: React.FC<{ row: RowInterface }> = (props) => ( <> <SmallText>Duration in ms:</SmallText> <InputMs row={props.row} /> </> ); const Rows: React.FC<RowProps> = (props) => { return ( <> {props.data.map((row) => <RowContent row={row} /> )} </> ) }; return( <Rows data={openItems} /> ) }; export default Page;
已尝试的方案
将输入框拆分为独立组件,但问题依旧:
const InputMs: React.FC<{ row: RowInterface }> = (props) => { const dispatch = useDispatch(); const setMs = (id: string, ms: number) => { dispatch(changeDuration({ id, duration: ms })); } return ( <InputNumber defaultValue={props.row.durationMs} onChange={(v) => setMs(props.row.id, v)} /> ) } const Page = () => { const openItems = ...get items from state; const RowContent: React.FC<{ row: RowInterface }> = (props) => ( <> <SmallText>Duration in ms:</SmallText> <InputMs row={props.row} /> </> ); const Rows: React.FC<RowProps> = (props) => { return ( <> {props.data.map((row) => <RowContent row={row} /> )} </> ) }; return (<Rows data={openItems} />) }; export default Page;
解决方案
核心思路是稳定组件类型、props引用,同时让React精准识别需要更新的列表项,具体步骤如下:
1. 提取子组件到父组件外部并使用React.memo
将InputMs、RowContent、Rows组件从Page内部移出,并用React.memo包裹。内部定义组件会导致每次父组件重渲染时创建全新的组件类型,触发子组件强制重渲染。
2. 给列表项添加唯一key
map渲染列表时,必须为每个项添加唯一的key(建议用数据项的id),让React能识别每个列表项的身份,仅重渲染变化的项。
3. 用useCallback稳定回调函数引用
回调函数如果直接定义在父组件中,每次重渲染都会生成新的引用,导致React.memo失效。用useCallback包裹回调,确保引用稳定。
4. 用value而非defaultValue绑定数据
defaultValue仅设置初始值,状态更新后不会同步到输入框。改用value绑定row.durationMs,确保输入框值与状态同步,避免重渲染时的值重置。
修改后的完整代码
import React, { memo, useCallback } from 'react'; import { InputNumber } from 'antd'; import { useDispatch } from 'react-redux'; // 独立组件+memo优化 const InputMs = memo(({ row, onDurationChange }) => { return ( <InputNumber value={row.durationMs} onChange={(v) => onDurationChange(row.id, v)} /> ); }); const RowContent = memo(({ row, onDurationChange }) => { return ( <> <SmallText>Duration in ms:</SmallText> <InputMs row={row} onDurationChange={onDurationChange} /> </> ); }); const Rows = memo(({ data, onDurationChange }) => { return ( <> {data.map((row) => ( <RowContent key={row.id} // 添加唯一key row={row} onDurationChange={onDurationChange} /> ))} </> ); }); const Page = () => { const dispatch = useDispatch(); const openItems = ...get items from state; // useCallback稳定回调引用 const onDurationChange = useCallback((id, ms) => { dispatch(changeDuration({ id, duration: ms })); }, [dispatch]); return <Rows data={openItems} onDurationChange={onDurationChange} />; }; export default Page;
效果说明
修改后,只有被修改的列表项会触发重渲染,其他项保持稳定;输入框在修改过程中不会失去焦点,同时值与状态实时同步。
内容的提问来源于stack exchange,提问作者1jss
相关产品推荐
相关产品推荐

