
在react中,当我们将表单元素(如<input>、<textarea>、<select>)的value属性绑定到组件状态,并通过onchange事件处理器更新该状态时,我们称之为“受控组件”。这种模式确保了表单数据始终由react状态管理,使得数据流清晰可控。然而,不当的受控组件实现方式可能导致一些意想不到的问题,其中最常见的就是输入框在每次按键后失焦。
原始代码中存在的核心问题在于,输入框的value属性直接来源于props.Freight,而props.Freight又直接映射到父组件的dataSource状态。当用户在输入框中键入一个字符时,handleOnchange函数被调用,它会立即更新dataSource状态。setDataSource的调用会导致持有dataSource的父组件重新渲染,进而导致其子组件,即gridTemplate,也随之重新渲染。
// 父组件中的状态与处理函数示例
// const [dataSource, setDataSource] = useState<any>(data);
// const handleOnchange = (event: any, props: any) => {
// const newData = [...dataSource];
// const itemIndex = newData.findIndex(
// (item) => item.OrderID === props.OrderID
// );
// newData[itemIndex].Freight = event.target.value;
// setDataSource(newData); // 每次按键都更新dataSource,导致父组件重渲染
// };
// Custom Grid Component (子组件)
const gridTemplate = (props: any) => {
const val = props.Freight; // val直接来源于父组件的props
return (
<div>
<input value={val} onChange={(event) => handleOnchange(event, props)} />
</div>
);
};每次setDataSource触发重渲染时,React会重新执行gridTemplate函数,并根据新的props.Freight值重新渲染input元素。尽管React的协调(reconciliation)算法会尽可能复用DOM元素,但在这种情况下,由于input的value属性在每次渲染时都可能发生变化,并且整个组件树(从父组件到gridTemplate)都在重新构建,React可能会认为需要重新挂载(remount)input元素,或者在重新应用属性时导致焦点丢失。这中断了用户在输入框中的连续输入体验。
解决此问题的关键在于,让输入框在用户输入过程中拥有自己的“瞬时”本地状态,而不是每次按键都立即同步到全局或父组件的状态。只有当用户完成输入(例如,通过失焦事件onBlur)或者通过防抖(debounce)机制时,才将这个最终值同步回父组件或全局数据源。
以下是修改后的gridTemplate组件,它内部维护了输入框的本地状态:
import React, { useState, useEffect } from 'react';
// Custom Grid Component (子组件)
const gridTemplate = (props: any) => {
// 使用useState管理输入框的本地值
const [inputValue, setInputValue] = useState(props.Freight);
// 当props.Freight从父组件更新时,同步本地inputValue
// 这很重要,以确保外部数据源更新时,输入框能反映最新值
useEffect(() => {
setInputValue(props.Freight);
}, [props.Freight]);
// 处理输入框的onChange事件,仅更新本地状态
const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setInputValue(event.target.value);
};
// 处理输入框的onBlur事件,此时才将本地值同步回父组件
const handleInputBlur = () => {
// 假设父组件提供一个更新特定项的函数,例如 props.updateItemFreight
// 这个函数会负责更新父组件的dataSource状态
if (props.updateItemFreight) {
props.updateItemFreight(props.OrderID, inputValue);
}
};
return (
<div>
<input
value={inputValue} // 绑定到本地状态
onChange={handleInputChange} // 仅更新本地状态
onBlur={handleInputBlur} // 失焦时才同步到父组件/全局状态
/>
</div>
);
};
// 假设父组件的用法示例:
// function ParentComponent() {
// const [dataSource, setDataSource] = useState<any>(/* 初始数据 */);
// const updateItemFreight = (orderId: string, newFreight: string) => {
// setDataSource(prevData => {
// const newData = [...prevData];
// const itemIndex = newData.findIndex(item => item.OrderID === orderId);
// if (itemIndex > -1) {
// newData[itemIndex].Freight = newFreight;
// }
// return newData;
// });
// };
// return (
// <div>
// {dataSource.map((item: any) => (
// <gridTemplate
// key={item.OrderID} // 确保有唯一的key
// OrderID={item.OrderID}
// Freight={item.Freight}
// updateItemFreight={updateItemFreight} // 传入更新函数
// />
// ))}
// </div>
// );
// }代码解释:
以上就是React受控组件:避免输入框失焦的常见陷阱与最佳实践的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号