哇,你真的很享受使用 React 附带的所有不同钩子的乐趣 ;-) 我看了你的代码和框大约 15 分钟。我的观点是,对于这样一个简单的任务,它的设计太过分了。没有恶意。我会做什么:
- 尝试退后一步,通过重构您的 index.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);
现在一切都是一个组件。如果您愿意,您现在可以将它重构为不同的组件,或者检查您可以应用什么样的钩子;-) 从简单开始并使其工作。然后你就可以继续剩下的了。
更新:
当您像这样更新 Friends 组件时:
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 组件的内部状态,从而导致表格重新呈现。