Reactjs:以编程方式关注以编程方式生成的输入
Reactjs: programmatically focusing on a programmatically generated input
我有一个呈现 editable table 的应用程序,每行有多个输入元素。由于 table 填充了用户条目,因此它可以是可变长度的。 table 中的某些用户条目将无效。当一个条目无效时,我想显示一个警告,上面写着 "You entered invalid data" 然后将用户集中在输入无效的条目上。
通读文档,ref 似乎是最好的做事方式。但是,我发现的所有示例,无论是来自 React 团队还是 Stack Overflow,都显示单个输入绑定到单个 ref。某些操作(通常是单击按钮)被硬编码为引用特定的 ref,并且一切正常。
我想确定在出现错误时访问相关输入的最佳方法。 'best' 方法似乎是创建一个引用数组,然后为每个输入分配一个引用——但文档也强调要谨慎使用引用,这感觉像是对参考
我更愿意做一些事情,比如创建一个 ref,然后将它重新分配给最近更新的任何输入。每次用户失去焦点时,它都会检查是否强制将焦点放在包含错误数据的输入上。这感觉它有可能成为一种反模式,尤其是因为我找不到任何其他人实施它的证据。
我很想知道解决这个问题的最佳方法,因为我担心我的两个想法都不是很好的前进道路。
您不能依赖输入的单个 ref,而是依赖容器 ref。你总是有一个容器引用,每当用户在输入上模糊时,你必须使用 .querySelectorAll('input')
从容器中获取所有输入,找到无效的并聚焦它。
const {
useState,
useCallback,
useMemo,
useRef,
useEffect
} = React;
const defaultUsers = [
{ id: 1, name: "User #1", email: "user1@gmail.com" },
{ id: 2, name: "User #2", email: "user2@gmail.com" },
{ id: 3, name: "User #3", email: "user3@gmail.com" },
{ id: 4, name: "User #4", email: "user4@gmail.com" },
{ id: 5, name: "", email: "user5@gmail.com" },
{ id: 6, name: "User #6", email: "user6@gmail.com" },
{ id: 7, name: "User #7", email: "user7@gmail.com" },
{ id: 8, name: "User #8", email: "" },
{ id: 9, name: "User #9", email: "user9@gmail.com" },
{ id: 10, name: "User #10", email: "user10@gmail.com" }
];
const App = () => {
const tbodyRef = useRef();
const focusNextInvalidInput = useCallback(() => {
if (tbodyRef.current) {
const inputs = tbodyRef.current.querySelectorAll("td > input");
for (const input of inputs) {
if (input.value.length === 0) {
input.focus();
return;
}
}
}
}, [tbodyRef]);
useEffect(() => {
focusNextInvalidInput();
}, [focusNextInvalidInput]);
const [users, setUsers] = useState(defaultUsers);
const updateUserField = useCallback(
(id, field, value) =>
setUsers(
users.map(user => (user.id === id ? { ...user, [field]: value } : user))
),
[users]
);
const changeUserName = useCallback(
(id, name) => updateUserField(id, "name", name),
[updateUserField]
);
const changeUserEmail = useCallback(
(id, email) => updateUserField(id, "email", email),
[updateUserField]
);
const usersRows = useMemo(() => {
return users.map(({ id, name, email }) => (
<tr key={id}>
<td>{id}</td>
<td>
<input
type="text"
onChange={e => changeUserName(id, e.target.value)}
value={name}
onBlur={focusNextInvalidInput}
/>
</td>
<td>
<input
type="email"
onChange={e => changeUserEmail(id, e.target.value)}
value={email}
onBlur={focusNextInvalidInput}
/>
</td>
</tr>
));
}, [users, changeUserEmail, changeUserName, focusNextInvalidInput]);
return (
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
</tr>
</thead>
<tbody ref={tbodyRef}>{usersRows}</tbody>
</table>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<div id="root"></div>
我有一个呈现 editable table 的应用程序,每行有多个输入元素。由于 table 填充了用户条目,因此它可以是可变长度的。 table 中的某些用户条目将无效。当一个条目无效时,我想显示一个警告,上面写着 "You entered invalid data" 然后将用户集中在输入无效的条目上。
通读文档,ref 似乎是最好的做事方式。但是,我发现的所有示例,无论是来自 React 团队还是 Stack Overflow,都显示单个输入绑定到单个 ref。某些操作(通常是单击按钮)被硬编码为引用特定的 ref,并且一切正常。
我想确定在出现错误时访问相关输入的最佳方法。 'best' 方法似乎是创建一个引用数组,然后为每个输入分配一个引用——但文档也强调要谨慎使用引用,这感觉像是对参考
我更愿意做一些事情,比如创建一个 ref,然后将它重新分配给最近更新的任何输入。每次用户失去焦点时,它都会检查是否强制将焦点放在包含错误数据的输入上。这感觉它有可能成为一种反模式,尤其是因为我找不到任何其他人实施它的证据。
我很想知道解决这个问题的最佳方法,因为我担心我的两个想法都不是很好的前进道路。
您不能依赖输入的单个 ref,而是依赖容器 ref。你总是有一个容器引用,每当用户在输入上模糊时,你必须使用 .querySelectorAll('input')
从容器中获取所有输入,找到无效的并聚焦它。
const {
useState,
useCallback,
useMemo,
useRef,
useEffect
} = React;
const defaultUsers = [
{ id: 1, name: "User #1", email: "user1@gmail.com" },
{ id: 2, name: "User #2", email: "user2@gmail.com" },
{ id: 3, name: "User #3", email: "user3@gmail.com" },
{ id: 4, name: "User #4", email: "user4@gmail.com" },
{ id: 5, name: "", email: "user5@gmail.com" },
{ id: 6, name: "User #6", email: "user6@gmail.com" },
{ id: 7, name: "User #7", email: "user7@gmail.com" },
{ id: 8, name: "User #8", email: "" },
{ id: 9, name: "User #9", email: "user9@gmail.com" },
{ id: 10, name: "User #10", email: "user10@gmail.com" }
];
const App = () => {
const tbodyRef = useRef();
const focusNextInvalidInput = useCallback(() => {
if (tbodyRef.current) {
const inputs = tbodyRef.current.querySelectorAll("td > input");
for (const input of inputs) {
if (input.value.length === 0) {
input.focus();
return;
}
}
}
}, [tbodyRef]);
useEffect(() => {
focusNextInvalidInput();
}, [focusNextInvalidInput]);
const [users, setUsers] = useState(defaultUsers);
const updateUserField = useCallback(
(id, field, value) =>
setUsers(
users.map(user => (user.id === id ? { ...user, [field]: value } : user))
),
[users]
);
const changeUserName = useCallback(
(id, name) => updateUserField(id, "name", name),
[updateUserField]
);
const changeUserEmail = useCallback(
(id, email) => updateUserField(id, "email", email),
[updateUserField]
);
const usersRows = useMemo(() => {
return users.map(({ id, name, email }) => (
<tr key={id}>
<td>{id}</td>
<td>
<input
type="text"
onChange={e => changeUserName(id, e.target.value)}
value={name}
onBlur={focusNextInvalidInput}
/>
</td>
<td>
<input
type="email"
onChange={e => changeUserEmail(id, e.target.value)}
value={email}
onBlur={focusNextInvalidInput}
/>
</td>
</tr>
));
}, [users, changeUserEmail, changeUserName, focusNextInvalidInput]);
return (
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
</tr>
</thead>
<tbody ref={tbodyRef}>{usersRows}</tbody>
</table>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<div id="root"></div>