反应动态表单输入useRef typeError
React dynamic form input useRef typeError
我有一个带有动态输入字段的 React 表单,用户可以添加和删除输入字段。我在提交表格时进行验证。如果输入为空,输入将通过 useRef 钩子获得焦点。问题是,如果我有两个输入为空,所以我添加了第二个输入并在之后将其删除,我得到 typeError“无法读取 null 的属性(读取 'focus')”。
App.js
import React, { useState, useRef } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
const [fields, setFields] = useState([""]);
const fieldRef = useRef();
const fieldsIsValid =
fields.length >= 1 && fields.every((field) => field.trim() !== "");
function handleChange(i, event) {
const values = [...fields];
values[i] = event.target.value;
setFields(values);
}
function handleAdd() {
const values = [...fields];
values.push("");
setFields(values);
}
function handleRemove(i) {
const values = [...fields];
values.splice(i, 1);
setFields(values);
}
function submitHandler(event) {
event.preventDefault();
if (!fieldsIsValid) {
if (fields.length >= 1) {
fieldRef.current.focus();
return;
}
return;
}
console.log(fields);
}
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<form onSubmit={submitHandler}>
<button type="button" onClick={() => handleAdd()}>
Add Input
</button>
{!fieldsIsValid && <p className="error">Input is required</p>}
{fields.map((field, idx) => {
return (
<div key={`${"input"}-${idx}`}>
<input
type="text"
placeholder="Enter text"
value={field || ""}
ref={fieldRef}
onChange={(e) => handleChange(idx, e)}
/>
<button type="button" onClick={() => handleRemove(idx)}>
X
</button>
</div>
);
})}
<button className="margin-top" type="submit">
Submit
</button>
</form>
</div>
);
}
export default App;
正如 Andreas 已经提到的,您需要为多个输入创建多个引用。 DOM 节点总是一对一 mapping/assignment 的 React 引用。如果您在多个位置使用相同的 ref,ref
将链接到您使用它的最后一个节点。为了帮助您理解这一点,请检查您在此处遇到的错误。它说,“无法读取 null
的属性”,即 fieldRef.current
的值为 null,因为 DOM 节点 fieldRef 最后映射到(最后一个输入字段)在删除后不存在DOM 树。
您可以尝试通过将 ref 放在表单标签而不是输入标签上来实现相同的功能,如下所示:
<form onSubmit={submitHandler} ref={fieldRef}>
function submitHandler(event) {
event.preventDefault();
for (let elem of fieldRef.current.elements) {
if (elem.type === 'text' && elem.dataset.required && !elem.value) {
elem.focus()
return
}
}
}
<input
type="text"
data-required="true" // <---- Add this here
placeholder="Enter text"
value={field || ""}
onChange={(e) => handleChange(idx, e)}
/>
我有一个带有动态输入字段的 React 表单,用户可以添加和删除输入字段。我在提交表格时进行验证。如果输入为空,输入将通过 useRef 钩子获得焦点。问题是,如果我有两个输入为空,所以我添加了第二个输入并在之后将其删除,我得到 typeError“无法读取 null 的属性(读取 'focus')”。
App.js
import React, { useState, useRef } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
const [fields, setFields] = useState([""]);
const fieldRef = useRef();
const fieldsIsValid =
fields.length >= 1 && fields.every((field) => field.trim() !== "");
function handleChange(i, event) {
const values = [...fields];
values[i] = event.target.value;
setFields(values);
}
function handleAdd() {
const values = [...fields];
values.push("");
setFields(values);
}
function handleRemove(i) {
const values = [...fields];
values.splice(i, 1);
setFields(values);
}
function submitHandler(event) {
event.preventDefault();
if (!fieldsIsValid) {
if (fields.length >= 1) {
fieldRef.current.focus();
return;
}
return;
}
console.log(fields);
}
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<form onSubmit={submitHandler}>
<button type="button" onClick={() => handleAdd()}>
Add Input
</button>
{!fieldsIsValid && <p className="error">Input is required</p>}
{fields.map((field, idx) => {
return (
<div key={`${"input"}-${idx}`}>
<input
type="text"
placeholder="Enter text"
value={field || ""}
ref={fieldRef}
onChange={(e) => handleChange(idx, e)}
/>
<button type="button" onClick={() => handleRemove(idx)}>
X
</button>
</div>
);
})}
<button className="margin-top" type="submit">
Submit
</button>
</form>
</div>
);
}
export default App;
正如 Andreas 已经提到的,您需要为多个输入创建多个引用。 DOM 节点总是一对一 mapping/assignment 的 React 引用。如果您在多个位置使用相同的 ref,ref
将链接到您使用它的最后一个节点。为了帮助您理解这一点,请检查您在此处遇到的错误。它说,“无法读取 null
的属性”,即 fieldRef.current
的值为 null,因为 DOM 节点 fieldRef 最后映射到(最后一个输入字段)在删除后不存在DOM 树。
您可以尝试通过将 ref 放在表单标签而不是输入标签上来实现相同的功能,如下所示:
<form onSubmit={submitHandler} ref={fieldRef}>
function submitHandler(event) {
event.preventDefault();
for (let elem of fieldRef.current.elements) {
if (elem.type === 'text' && elem.dataset.required && !elem.value) {
elem.focus()
return
}
}
}
<input
type="text"
data-required="true" // <---- Add this here
placeholder="Enter text"
value={field || ""}
onChange={(e) => handleChange(idx, e)}
/>