我有这样的场景,我从服务器加载表单的数据(假设一个带有用户朋友列表的用户实体)。
表单具有可编辑名称的好友列表,这些朋友将其呈现为带有 react-table 7 的表。 我面临的问题是,每当我尝试编辑此列表中朋友的名字时,我只能键入一个字符,然后输入就会失去焦点。我再次单击输入,键入 1 个字符,它再次失去焦点。
我创建了一个代码沙箱来说明这个问题:https://codesandbox.io/s/formik-react-table-hr1l4
我理解为什么会发生这种情况 - 每次我键入时表格都会重新呈现,因为 formik 状态发生变化 - 但我不确定如何防止这种情况发生。我useMemo
- ed和useCallback
- ed我能想到的所有内容(也React.memo
- ed组件,希望它可以防止问题),但到目前为止没有运气。
如果我删除Friends
中的useEffect
,它确实有效,但是,这将使表在超时到期后不更新(因此它不会在 2 秒后显示 1 个朋友)。 任何帮助都非常感谢...我一整天都在这个问题上呆着。
哇,你真的在使用 React 附带的所有不同钩子时玩得很开心;-)我看了你的代码沙箱大约 15 分钟了。我的观点是,对于如此简单的任务,它被过度设计了。没有冒犯。我会做什么:
- 尝试退后一步,通过重构索引从简单开始.js并按照 Formik 主页上的预期使用该
FieldArray
(每个朋友一个渲染)。 - 下一步,您可以围绕它构建一个简单的表格
- 然后,您可以尝试使用输入字段使不同的字段可编辑
- 如果你真的需要它,你可以添加
react-table
库,但我认为没有它应该很容易实现它
这里有一些代码来向您展示我的意思:
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import { Formik, Form, FieldArray, Field } from "formik";
import Input from "./Input";
import "./styles.css";
const initialFormData = undefined;
function App() {
const [formData, setFormData] = useState(initialFormData);
useEffect(() => {
// this is replacement for a network call that would load the data from a server
setTimeout(() => {
setFormData({
id: 1,
firstName: "First Name 1",
friends: [
{ id: 2, firstName: "First Name 2", lastName: "Last Name 2" },
{ id: 3, firstName: "First Name 3", lastName: "Last Name 3" }
]
});
}, 1000);
// Missing dependency array here
}, []);
return (
<div className="app">
{formData && (
<Formik initialValues={formData} enableReinitialize>
{({ values }) => (
<Form>
<Input name="name" label="Name: " />
<FieldArray name="friends">
{arrayHelpers => (
<div>
<button
onClick={() =>
arrayHelpers.push({
id: Math.floor(Math.random() * 100) / 10,
firstName: "",
lastName: ""
})
}
>
add
</button>
<table>
<thead>
<tr>
<th>ID</th>
<th>FirstName</th>
<th>LastName</th>
<th />
</tr>
</thead>
<tbody>
{values.friends && values.friends.length > 0 ? (
values.friends.map((friend, index) => (
<tr key={index}>
<td>{friend.id}</td>
<td>
<Input name={`friends[${index}].firstName`} />
</td>
<td>
<Input name={`friends[${index}].lastName`} />
</td>
<td>
<button
onClick={() => arrayHelpers.remove(index)}
>
remove
</button>
</td>
</tr>
))
) : (
<tr>
<td>no friends :(</td>
</tr>
)}
</tbody>
</table>
</div>
)}
</FieldArray>
</Form>
)}
</Formik>
)}
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
现在一切都是一个组成部分。如果您愿意,您现在可以将其重构为不同的组件,或者检查您可以应用哪种钩子;-)从简单开始,让它发挥作用。然后你可以继续剩下的。
更新:
当您像这样更新好友组件时:
import React, { useCallback, useMemo } from "react";
import { useFormikContext, getIn } from "formik";
import Table from "./Table";
import Input from "./Input";
const EMPTY_ARR = [];
function Friends({ name, handleAdd, handleRemove }) {
const { values } = useFormikContext();
// from all the form values we only need the "friends" part.
// we use getIn and not values[name] for the case when name is a path like `social.facebook`
const formikSlice = getIn(values, name) || EMPTY_ARR;
const onAdd = useCallback(() => {
const item = {
id: Math.floor(Math.random() * 100) / 10,
firstName: "",
lastName: ""
};
handleAdd(item);
}, [handleAdd]);
const onRemove = useCallback(
index => {
handleRemove(index);
},
[handleRemove]
);
const columns = useMemo(
() => [
{
Header: "Id",
accessor: "id"
},
{
Header: "First Name",
id: "firstName",
Cell: ({ row: { index } }) => (
<Input name={`${name}[${index}].firstName`} />
)
},
{
Header: "Last Name",
id: "lastName",
Cell: ({ row: { index } }) => (
<Input name={`${name}[${index}].lastName`} />
)
},
{
Header: "Actions",
id: "actions",
Cell: ({ row: { index } }) => (
<button type="button" onClick={() => onRemove(index)}>
delete
</button>
)
}
],
[name, onRemove]
);
return (
<div className="field">
<div>
Friends:{" "}
<button type="button" onClick={onAdd}>
add
</button>
</div>
<Table data={formikSlice} columns={columns} rowKey="id" />
</div>
);
}
export default React.memo(Friends);
它似乎不再失去焦点。你也可以检查一下吗?我删除了useEffect块,该表直接与formikSlice
一起使用。我想问题是当您更改输入时,Formik 值已更新,并且触发了 useEffect 块以更新 Friends 组件的内部状态,从而导致表重新渲染。