识别 Formik FieldArray 中已删除(创建和修改)的项目

IT技术 javascript reactjs formik
2021-04-28 04:47:49

想知道 Formik 是否有一个本地解决方案来识别FieldArray表单中的添加和删除(和更新)

我在沙箱上有代码https://codesandbox.io/s/jn7x2m75o9 (基于原始 Formik Array 示例 @ https://github.com/jaredpalmer/formik/blob/master/examples/Arrays.js

还有这里的相关部分:

随着初始state3朋友定义,我怎么能知道我的onSubmithandler哪一个被修改,删除,更新

import React from "react";
import { Formik, Field, Form, ErrorMessage, FieldArray } from "formik";

const initialValues = {
  friends: [
    {
      name: "Friend_A",
      email: "email_A@somewhere.com"
    },
    {
      name: "Friend_B",
      email: "email_B@somewhere.com"
    },
    {
      name: "Friend_C",
      email: "email_C@somewhere.com"
    }
  ]
};

const mySubmit = values => console.log();

const SignIn = () => (
  <div>
    <h1>Invite friends</h1>
    <Formik
      initialValues={initialValues}
      onSubmit={values => {
        var itemRemoved = values.GetItemRemoveFromArray; // This is what I'm looking for
        console.log(itemRemoved);
        // Would print Friend_A

        var itemAdded = values.GetItemAddedFromArray; // This is what I'm looking for
        console.log(itemAdded);
        // Would print New_Friend

        var itemUpdated = values.GetItemUpdatedInArray; // This is what I'm looking for
        console.log(itemUpdated);
        // Would print Friend_C

        setTimeout(() => {
          alert(JSON.stringify(values, null, 2));
        }, 500);
      }}
      render={({ values }) => (
        <Form>
          <FieldArray
            name="friends"
            render={({ insert, remove, push }) => (
              <div>
                {values.friends.length > 0 &&
                  values.friends.map((friend, index) => (
                    <div className="row" key={index}>
                      <div className="col">
                        <label htmlFor={`friends.${index}.name`}>Name</label>
                        <Field
                          name={`friends.${index}.name`}
                          placeholder="Jane Doe"
                          type="text"
                        />
                        <ErrorMessage
                          name={`friends.${index}.name`}
                          component="div"
                          className="field-error"
                        />
                      </div>
                      <div className="col">
                        <label htmlFor={`friends.${index}.email`}>Email</label>
                        <Field
                          name={`friends.${index}.email`}
                          placeholder="jane@acme.com"
                          type="email"
                        />
                        <ErrorMessage
                          name={`friends.${index}.name`}
                          component="div"
                          className="field-error"
                        />
                      </div>
                      <div className="col">
                        <button
                          type="button"
                          className="secondary"
                          onClick={() => remove(index)}
                        >
                          X
                        </button>
                      </div>
                    </div>
                  ))}
                <button
                  type="button"
                  className="secondary"
                  onClick={() => push({ name: "", email: "" })}
                >
                  Add Friend
                </button>
              </div>
            )}
          />
          <button type="submit">Invite</button>
        </Form>
      )}
    />
  </div>
);

export default SignIn;

因此,如果使用上述用户在哪里:

  1. 点击Friend_A下方的X
  2. 将 Friend_C 电子邮件修改为 email_C@nothere.com
  3. 点击“添加好友”
  4. 输入值名称:New_Friend_X 和电子邮件:XX@YY.com
  5. 点击“添加好友”
  6. 输入值名称:New_Friend_Z 和电子邮件:Friend_Z@coolplace.com
  7. 点击新输入的“New_Friend_X”下方的“X”按钮
  8. 点击“邀请”

在我的 mySubmit 中,我正在寻找一种轻松获取的方法:

  1. Friend_A 已删除
  2. Friend_C 被修改
  3. 添加了 New_Friend_Z(不在 formik 的原始初始值中)

(我不在乎 New_Friend_X。不需要知道它被添加/删除了)

这一点是为了尽量减少对后端的调用以创建/更新实体/链接,而且我真的不想onClick在调用Formikremove(index)提供处理程序来跟踪什么之前在删除按钮处理程序中编写我自己的“辅助状态”需要从数据库中删除。

1个回答

它没有内置于 Formik 中,但在 javascript 中并不难做到。

首先,了解 Formik 会克隆您提供给 的对象initialValues因此,在 中onSubmit,您将最终值与原始对象进行比较。

传入数据:

const initialFriends = [
  {
    name: "Friend_A",
    email: "email_A@somewhere.com"
  },
  {
    name: "Friend_B",
    email: "email_B@somewhere.com"
  },
  {
    name: "Friend_C",
    email: "email_C@somewhere.com"
  }
];

const initialValues = { friends: initialFriends };

修改 Formik 声明:

<Formik initialValues={initialValues}
  ...
  onSubmit={values => {
    const { added, deleted, changed } = addDeleteChange(
      initialFriends,
      values.friends
    );

    setTimeout(() => {
      alert(
        "Added: " + JSON.stringify(Object.fromEntries(added.entries()))
      );
      alert(
        "Deleted: " + JSON.stringify(Object.fromEntries(deleted.entries()))
      );
      alert(
        "Changed:" + JSON.stringify(Object.fromEntries(changed.entries()))
      );
      alert(JSON.stringify(values, null, 2));
    }, 500);
  }}
  ...

辅助功能:

function partition(array, filter) {
  let pass = [],
    fail = [];
  array.forEach(e => (filter(e) ? pass : fail).push(e));
  return [pass, fail];
}

const addDeleteChange = (in1, out1) => {
  let inMap = new Map(in1.map(f => [f.name, f]));
  let outMap = new Map(out1.map(f => [f.name, f]));
  let inNames = new Set(inMap.keys());
  let outNames = new Set(outMap.keys());
  let [kept, added] = partition(out1, f => inNames.has(f.name));
  let deleted = in1.filter(f => !outNames.has(f.name));
  //alert(JSON.stringify(Object.fromEntries(deleted.entries())));
  let changed = kept.filter(f => f.email !== inMap.get(f.name).email);
  //alert(JSON.stringify(Object.fromEntries(changed.entries())));
  return { added: added, deleted: deleted, changed: changed };
};

代码沙盒中的代码


注意:如果您更改朋友姓名,则会显示为删除原始朋友和添加新朋友。
更强大的解决方案是为每个朋友添加一个(隐藏的)“id”字段。然后不是比较name而是比较id
这需要在添加每个朋友时生成一个新 id。