У меня есть компонент автосохранения в реакции, как мне проверить реквизиты saveData в сравнении с предыдущим состоянием, чтобы я брал только новые значения

У меня есть компонент автоматического сохранения в нижней части кода ниже, он принимает свойство saveData, которое является состоянием значений таблиц, это состояние обновляется при размытии при редактировании полей. Теперь я хочу взять эти saveData и сравнить их с предыдущим состоянием после onblur, поэтому я беру только измененные значения ключей и запускаю мутацию, чтобы сохранить их. В противном случае мне пришлось бы запускать мутацию с каждым значением, и это было бы довольно много, когда таблица была бы заполнена.

Спасибо за любую помощь заранее.

Я пытаюсь сравнить старое состояние с новым состоянием здесь, так что это может быть лучшим местом для начала.

У меня есть два массива с объектами внутри, в которых я хочу сравнить значения и вернуть изменение значения ключа и позиции массива

Вот мой код до сих пор


const DECOR_VALUES = gql`
  query GetDecorValues($id: ID!) {
    findUserByID(id: $id) {
      decor {
        data {
          purchaseDate
          description
          alterations
          cost
          pieces
          category
          purchaser
          image
          itemNum
          _id
        }
      }
    }
  }
`;

const UPDATE_DECOR_DOC = gql`
  mutation UpdateDecorDoc(
    # $ownerID: ID!
    $description: String!
    $pieces: Int!
    $purchaser: String!
    $alterations: Boolean!
    $cost: Int!
    $purchaseDate: Date!
    $category: String!
    $image: String!
    $itemNum: Int!
  ) {
    updateDecor(
      data: {
        description: $description
        pieces: $pieces
        purchaser: $purchaser
        alterations: $alterations
        cost: $cost
        purchaseDate: $purchaseDate
        category: $category
        image: $image
        itemNum: $itemNum
        # owner: { connect: $ownerID }
      }
    ) {
      description
    }
  }
`;

const Styles = styled.div`
  padding: 1rem;

  table {
    border-spacing: 0;
    border: 1px solid black;
    tr {
      :last-child {
        td {
          border-bottom: 0;
        }
      }
    }

    th,
    td {
      margin: 0;
      padding: 0.5rem;
      border-bottom: 1px solid black;
      border-right: 1px solid black;

      :last-child {
        border-right: 0;
      }

      input {
        font-size: 1rem;
        padding: 0;
        margin: 0;
        border: 0;
      }
      &:hover {
        background: lightpink;
      }
    }
  }

  .pagination {
    padding: 0.5rem;
  }
`;

// Create an editable cell renderer
const EditableCell = ({
  value: initialValue,
  row: { index },
  column: { id },
  updateMyData, // This is a custom function that we supplied to our table instance
}) => {
  // We need to keep and update the state of the cell normally
  const [value, setValue] = React.useState(initialValue);

  const onChange = (e) => {
    setValue(e.target.value);
  };
  const onChangeDate = (e) => {
    setValue(e);
  };

  const onChangeCheck = (e) => {
    setValue(e.target.checked);
  };

  // We'll only update the external data when the input is blurred
  const onBlur = () => {
    updateMyData(index, id, value);
  };

  // If the initialValue is changed external, sync it up with our state
  React.useEffect(() => {
    setValue(initialValue);
  }, [initialValue]);

  if (id === "col3") {
    return (
      <input
        type="checkbox"
        defaultChecked={value}
        onChange={onChangeCheck}
        onBlur={onBlur}
      />
    );
  }

  if (id === "col1") {
    return (
      <ReactDatePicker
        onCalendarClose={onBlur}
        selected={new Date(value)}
        onChange={onChangeDate}
      />
    );
  }

  return <input value={value} onChange={onChange} onBlur={onBlur} />;
};

// Set our editable cell renderer as the default Cell renderer
const defaultColumn = {
  Cell: EditableCell,
};

const fetcher = (url) => fetch(url).then((r) => r.json());

export default function DecorData() {
  const { data: user, error: userError } = useSWR("/api/user", fetcher);

  const { data: cookieData, error: cookieError } = useSWR(
    "/api/cookie",
    fetcher
  );

  var cookieBearer = `Bearer ${cookieData}`;

  if (!user || !cookieData) return <p>Loading</p>;
  if (userError) return <p>{userError.message}</p>;
  if (cookieError) return <p>{cookieError.message}</p>;

  return (
    <Layout>
      <h1>View your Decor Catalog Table Here</h1>
      {user && cookieBearer && (
        <Table user={user} cookieBearer={cookieBearer} />
      )}
    </Layout>
  );
}

const Table = ({ user, cookieBearer }) => {
  const { loading, error, data: decorData } = useQuery(DECOR_VALUES, {
    variables: { id: user.id },
    context: {
      headers: {
        authorization: cookieBearer,
      },
    },
  });

  const massaged = decorData?.findUserByID?.decor?.data?.map((item) => {
    var col = Object.values(item);
    console.log("item");
    console.log(decorData);
    console.log("item");
    return col.map((colItem, i) => {
      return { [`col${i}`]: colItem };
    });
  });

  const massaged1 = decorData?.findUserByID?.decor?.data?.map((item, int) => {
    var col = Object.keys(item);
    return col?.map((colItem, i) => {
      console.log(colItem);
      if (colItem === "image") {
        return {
          Header: colItem,
          accessor: `col${i}`,
          Cell: ({ cell: { value } }) => <ImageComp value={value} />,
        };
      }
      return {
        Header: colItem,
        accessor: `col${i}`,
      };
    });
  });

  const result = massaged?.map((a) => Object.assign({}, ...a));

  if (loading) return <p>Loading</p>;
  if (error) return <p>{error.message}</p>;
  if (!decorData) return <p>No Decord Data</p>;

  return (
    <>
      {result && massaged1 && <TryThis cookieBearer={cookieBearer} result={result} massaged1={massaged1} />}
    </>
  );
};

const ImageComp = ({ value }) => {
  return (
    <>
      <img src={value} width="400" />
      <button>Replace Image</button>
    </>
  );
};

function TryThis({ result, massaged1, cookieBearer }) {
  const [data, setData] = React.useState(result); //bad naming here
  // const [originalData] = React.useState(data)
  const [skipPageReset, setSkipPageReset] = React.useState(false);

  const updateMyData = (rowIndex, columnId, value) => {
    // We also turn on the flag to not reset the page
    setSkipPageReset(true);
    setData((old) => {
      // console.log(old);
      return old.map((row, index) => {
        if (index === rowIndex) {
          return {
            ...old[rowIndex],
            [columnId]: value,
          };
        }
        return row;
      });
    });
  };

  // After data chagnes, we turn the flag back off
  // so that if data actually changes when we're not
  // editing it, the page is reset
  React.useEffect(() => {
    setSkipPageReset(false);
  }, [data]);

  return (
    <>
      {/* {result && massaged1 && (
        <TableRendered result={result} massaged1={massaged1} />
      )} */}
      {data && result && massaged1 && (
        <Styles>
          {/* <button onClick={resetData}>Reset Data</button> */}
          <Table1
            columns={massaged1[0]}
            data={data}
            updateMyData={updateMyData}
            skipPageReset={skipPageReset}
            cookieBearer={cookieBearer}
          />
        </Styles>
      )}
    </>
  );
}

function Table1({ columns, data, updateMyData, skipPageReset, cookieBearer }) {
  // For this example, we're using pagination to illustrate how to stop
  // the current page from resetting when our data changes
  // Otherwise, nothing is different here.
  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    prepareRow,
    page,
    canPreviousPage,
    canNextPage,
    pageOptions,
    pageCount,
    gotoPage,
    nextPage,
    previousPage,
    setPageSize,
    state: { pageIndex, pageSize },
  } = useTable(
    {
      columns,
      data,
      defaultColumn,
      // use the skipPageReset option to disable page resetting temporarily
      autoResetPage: !skipPageReset,
      // updateMyData isn't part of the API, but
      // anything we put into these options will
      // automatically be available on the instance.
      // That way we can call this function from our
      // cell renderer!
      updateMyData,
    },
    usePagination
  );

  // Render the UI for your table
  return (
    <>
      <table {...getTableProps()}>
        <thead>
          {headerGroups.map((headerGroup) => (
            <tr {...headerGroup.getHeaderGroupProps()}>
              {headerGroup.headers.map((column) => (
                <th {...column.getHeaderProps()}>{column.render("Header")}</th>
              ))}
            </tr>
          ))}
        </thead>
        <tbody {...getTableBodyProps()}>
          {page.map((row, i) => {
            prepareRow(row);
            return (
              <tr {...row.getRowProps()}>
                {row.cells.map((cell) => {
                  return (
                    <td {...cell.getCellProps()}>{cell.render("Cell")}</td>
                  );
                })}
              </tr>
            );
          })}
        </tbody>
      </table>
      <div className="pagination">
        <button onClick={() => gotoPage(0)} disabled={!canPreviousPage}>
          {"<<"}
        </button>{" "}
        <button onClick={() => previousPage()} disabled={!canPreviousPage}>
          {"<"}
        </button>{" "}
        <button onClick={() => nextPage()} disabled={!canNextPage}>
          {">"}
        </button>{" "}
        <button onClick={() => gotoPage(pageCount - 1)} disabled={!canNextPage}>
          {">>"}
        </button>{" "}
        <span>
          Page{" "}
          <strong>
            {pageIndex + 1} of {pageOptions.length}
          </strong>{" "}
        </span>
        <span>
          | Go to page:{" "}
          <input
            type="number"
            defaultValue={pageIndex + 1}
            onChange={(e) => {
              const page = e.target.value ? Number(e.target.value) - 1 : 0;
              gotoPage(page);
            }}
            style={{ width: "100px" }}
          />
        </span>{" "}
        <select
          value={pageSize}
          onChange={(e) => {
            setPageSize(Number(e.target.value));
          }}
        >
          {[10, 20, 30, 40, 50].map((pageSize) => (
            <option key={pageSize} value={pageSize}>
              Show {pageSize}
            </option>
          ))}
        </select>
      </div>
      <pre>
        <code>
          {JSON.stringify(
            {
              data,
            },
            null,
            2
          )}
        </code>
      </pre>
      {cookieBearer&& <AutoSave cookieBearer={cookieBearer} saveData={data} />}
    </>
  );
}

function AutoSave({ saveData, cookieBearer }) {
  const [saving, setSaving] = useState(false);

  const [
    updateDecorDoc,
    { data: docData, loading: saving },
  ] = useMutation(UPDATE_DECOR_DOC, {
    context: {
      headers: {
        authorization: cookieBearer,
      },
    },
  });

  const debounceSave = useCallback(
    debounce(async (saveData) => {
      console.log("save data");
      console.log(saveData);
      console.log("save data");
    })
  );

  useEffect(() => {
    if (saveData) {
      debounceSave(saveData);
    }
  }, [saveData, debounceSave]);

 return null
}

person Anders Kitson    schedule 13.03.2021    source источник