Identifying what item have been deleted (created and modifed) in a Formik FieldArray

给你一囗甜甜゛ 提交于 2020-01-24 20:49:12

问题


Was wondering if Formik has a native solution for identifying the addition and deletion (and update) of FieldArray in the form ?

I have the code on sandbox here https://codesandbox.io/s/jn7x2m75o9 ( based on the original Formik Array example @ https://github.com/jaredpalmer/formik/blob/master/examples/Arrays.js )

but also the relevant part here :

With an Initial state of 3 friend defined, how can I know in my onSubmithandler which one were modified,deleted,updated.

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;

So if with the above a user where to :

  1. Click on the X below Friend_A
  2. Modify Friend_C email to email_C@nothere.com
  3. Click "Add Friend"
  4. Enter value Name: New_Friend_X and email: XX@YY.com
  5. Click "Add Friend"
  6. Enter value Name: New_Friend_Z and email: Friend_Z@coolplace.com
  7. Click "X" button below newly entered "New_Friend_X"
  8. Click "Invite"

in my mySubmit I'm looking for a way to easily get :

  1. Friend_A was Removed
  2. Friend_C was Modified
  3. New_Friend_Z was added (was not in the original initialValues to formik)

(I Don't care about New_Friend_X. No need to know it was added/removed )

Point of this is to minimize rest call to the back end to create/update entity/link and also I really dont want to write my own "secondary state" in the onClick handler of the remove button before calling the remove(index) handler provided by Formik to track what need to be deleted from the DB.


回答1:


Its not built into Formik, but it is not hard to do in javascript.

First, understand that Formik clones the object you give to initialValues. So in onSubmit, you will compare the final value to your original object.

The incoming data:

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 };

Modified Formik declaration:

<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);
  }}
  ...

Helper functions:

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 };
};

Code in codesandbox


NOTE: If you change the name of a friend, that will appear as a delete of original friend and an add of a new friend.
A more robust solution would be to add a (hidden) "id" field to each friend. Then instead of comparing name, would compare id.
That requires generating a new id as add each friend.



来源:https://stackoverflow.com/questions/54876136/identifying-what-item-have-been-deleted-created-and-modifed-in-a-formik-fielda

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!