繁体   English   中英

useState() 钩子在设置新值时从 object 中删除字段

[英]useState() hook removes fields from the object when setting new value

无法理解我在这里做错了什么。 我有一个非常简单的 object state:

const [itemInput, setItemInput] = useState({
    userId: userId,
    objectId: id,
    title: "",
    description: "",
    createdAt: new Date().getTime().toString(),
  })

然后,我有一个更改title字段的输入:

const onChange = (e) => {
    setItemInput((prevState) => ({
      ...prevState,
      [e.target.name]: e.target.value,
    }));
  };

这就是onChange的使用方式:

<input
        type="text"
        className="item-description-input"
        placeholder="Add description..."
        name="title"
        value={itemInput.title}
        onChange={onChange}
      />

GraphQL 端点:

export const CREATE_ITEM = gql`
  mutation createItem($itemInput: ItemInput!) {
    createItem(itemInput: $itemInput) {
      userId
      objectId
      title
      description
      createdAt
    }
  }
`;

提交 function:

const onAddItemClick = (e) => {
    e.preventDefault();
    createItem({
      variables: {
        itemInput: itemInput,
      },
    }).then((res) => {
      console.log(res);
    });
  };

提交时,我得到:

message: "Variable "$itemInput" got invalid value { title: "ddd" }; Field "userId" of required type "MongoId!" was not provided." 

并且除了title之外的所有剩余字段都会重复此错误。

如果我在加载功能组件时使用console.log(itemInput) ,我会得到 object 的初始 state。 但是,如果我在onChange之后添加console.log(itemInput) ,我只会得到{title: "some_added_text"} onChange 从 object 中删除其他字段,并且在提交时,我的 graphQL 端点返回未提供其他字段的错误。 这非常令人沮丧,因为我似乎做的一切都是正确的。

UPD:添加了组件。 Object组件获取对象列表。 每个Object都有Items 当我想向该Object添加新Item时,有问题的代码位于Items组件中。

Object(父)组件:

import React, { useState, useReactiveVar, Fragment } from "react";
import { useMutation } from "@apollo/client";
import CREATE_OBJECT from "../../mutations/Object";
import { objectsVar } from "../../cache";

//Components
import ItemContainer from "./ItemContainer";

const Container = ({ data, userId, color }) => {
  const [objectInput, setobjectInput] = useState({
    title: "",
    userId: userId,
    createdAt: new Date().getTime().toString(),
    color: color,
  });

  const [createObject] = useMutation(CREATE_OBJECT);

  const onAddClick = (e) => {
    e.preventDefault();
    createObject({
      variables: {
        objectInput: objectInput,
      },
    }).then((res) => {
      setobjectInput({
        title: "",
        userId: userId,
        createdAt: new Date().getTime().toString(),
        color: color,
      });
    });
  };
  
  // This works perfectly fine!! 
  const onChange = (e) => {
    setobjectInput({ ...objectInput, [e.target.name]: e.target.value });
  };

  objectsVar(data);
  const objects = useReactiveVar(objectsVar);

  return (
    <div>
      <div>
        <input
          type="text"
          placeholder="Add description..."
          name="title"
          value={objectInput.title}
          onChange={onChange}
        />
      </div>
      <div onClick={onAddClick}>Add object</div>
      {objects && objects.getAllObjects.length === 0 ? (
        <p>No objects</p>
      ) : (
        <Fragment>
          {objects &&
            objects.getAllActiveSwimlines.map(({ _id, title, items }) => (
              <ItemContainer key={_id} id={_id} title={title} userId={userId} items={items} />
            ))}
        </Fragment>
      )}
    </div>
  );
};

export default Container;

项目(子)组件:

import React, { useState, Fragment } from "react";
import ItemContainer from "../Item/ItemContainer";
import { useMutation } from "@apollo/client";
import { CREATE_ITEM } from "../../mutations/Item";

const ItemContainer = ({ id, title, items, userId }) => {
  const [itemInput, setItemInput] = useState({
    userId: userId,
    objectId: id,
    title: "initial state",
    description: "test desc",
    createdAt: new Date().getTime().toString(),
  });

  // This doesn't work!
  const onChange = (e) => {
    const { name, value } = e.target;
    setItemInput({
      ...itemInput,
      [name]: value,
    });
  };

  const [createItem] = useMutation(CREATE_ITEM);

  const onAddItemClick = (e) => {
    e.preventDefault();
    createItem({
      variables: {
        itemInput: itemInput,
      },
    }).then((res) => {
      setItemInput({
        userId: userId,
        objectId: id,
        title: "",
        description: "",
        createdAt: new Date().getTime().toString(),
      });

      console.log(res);
    });
  };

  const newItemInput = (
    <div>
      <input
        type="text"
        placeholder="Add description..."
        name="title"
        value={itemInput.title}
        onChange={onChange}
      />
      <div onClick={onAddItemClick}>Add item</div>
    </div>
  );

  const itemContent = (
    <div>
      <Fragment>
        {items &&
          items.getObjectItems.map(({ _id, title }) => (
            <Item key={_id} id={_id} title={title} />
          ))}
      </Fragment>
    </div>
  );

  return (
    <div>
      {newItemInput}
      {itemContent}
    </div>
  );
};

export default ItemContainer;

我在这里想念什么?

因为您在更新 function 中访问e.target.namee.target.value ,所以不会立即对它们进行评估,而当他们这样做时,合成事件 object 已被重用。 如果您想保持代码的 rest 不变,可以通过在onChange开头调用e.persist()来修复它,或者从事件中复制相关信息:

  const onChange = (e) => {
    const {name, value} = e.target
    setItemInput((prevState) => ({
      ...prevState,
      [name]: value,
    }));
  };

或者,您可以让更新 function 的 go 并直接设置 state ,(在已删除的其他答案中也建议):

  const onChange = (e) => {
    setItemInput({
      ...itemInput,
      [e.target.name]: e.target.value,
    });
  };

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM