【问题标题】:How do I put an initialValue taken from the useEffect in the textField?如何将从 useEffect 中获取的 initialValue 放在 textField 中?
【发布时间】:2021-08-21 05:58:49
【问题描述】:

我有这个编辑页面,其中名字、姓氏和地址的初始值应该来自使用 useEffect 从 Firebase Firestore 检索到的数据。

 useEffect(() => {
    const unsubscribe = firestore
      .collection("users")
      .doc(uid)
      .onSnapshot((snapshot) => {
        const arr = [];
        arr.push({
          ...snapshot.data(),
        });

        setUsers(arr);
      });

    return () => {
      unsubscribe();
    };
  }, []);

handleSubmit 将更新的信息保存在 Firestore 中:

  const handleSubmit = async (e) => {
    e.preventDefault();

    try {
      const userRef = firestore.collection("users").doc(uid);
      const ref = userRef.set(
        {
          firstName,
          middleName,
          lastName,
          address,
        },
        { merge: true }
      );
      console.log(" saved");
    } catch (err) {
      console.log(err);
    }
  };

下面是完整的代码:

const edit = () => {
  const uid = location.state;
  const [isLoading, setIsLoading] = useState(false);
  const [users, setUsers] = useState([]);
  const [firstName, setFirstName] = useState("");

  useEffect(() => {
    const unsubscribe = firestore
      .collection("users")
      .doc(uid)
      .onSnapshot((snapshot) => {
        const arr = [];
        arr.push({
          ...snapshot.data(),
        });

        setUsers(arr);
        setIsLoading(true);
      });

    return () => {
      unsubscribe();
    };
  }, []);

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log(firstName);
  };

  return (
    <div>
        {isLoading ? (
          <>
            {users &&
              users.map((user) => (
                <li style={{ listStyle: "none" }}>
                  <CardHeader title="Update Profile" />
                  <form onSubmit={handleSubmit}>
                    <TextField
                      type="text"
                      value={user.firstName}
                      variant="outlined"
                      label="First Name"
                      fullWidth
                      onChange={(e) => setFirstName(e.target.value)}
                    />
                    <Button type="submit"> Submit</Button>
                  </form>
                </li>
              ))}
          </>
        ) : (
          <h1>Loading...</h1>
        )}
    </div>
  );
};

export default edit;

假设我想编辑用户“John Park”的地址。这些文本字段应该具有 John 的名字和姓氏的初始值,因此即使地址字段是唯一更新的字段,当保存在 firestore 中时,名字和姓氏字段也不会是空字符串。我该怎么做?谢谢。

【问题讨论】:

  • 可以提供沙盒吗?
  • 您不能使用 useState 挂钩来存储值并在输入更改时更改它们,然后直接在 handlesubmit 函数中传递它们吗?
  • 我有点同意@user11823877...自从使用value 属性以来,您已经控制了输入,但我没有看到任何onChange 处理程序。
  • @user11823877 这是什么意思?
  • @DrewReese 我已经更新了代码,虽然当我控制台记录它时没有第一个名字的输出

标签: javascript reactjs firebase google-cloud-firestore material-ui


【解决方案1】:

您似乎正在更新本地状态,但它没有初始化为空字符串。

您的更新应该是您获取的users 的本地副本,并且提交处理程序应该在您的后端获取特定用户进行更新。

const [users, setUsers] = React.useState([]);

更改处理程序 - 用于获取索引和返回处理程序的柯里化函数。它将先前的users 状态映射到下一个状态,更新指定索引处的用户。

const changeHandler = index => e => {
  const { name, value } = e.target;
  setUsers(users => users.map((user, i) => i === index
    ? {
      ...user,
      [name]: value,
    }
    : user,
  ));
};

提交处理程序 - curried 函数以获取特定用户对象的索引以发送到后端。

const handleSubmit = index => async (e) => {
  e.preventDefault();

  try {
    const userRef = firestore.collection("users").doc(uid);
    const ref = userRef.set(
      { ...users[index] }, // <-- user by index
      { merge: true }
    );
    console.log(" saved");
  } catch (err) {
    console.log(err);
  }
};

将当前映射的索引传递给处理程序。将name 属性添加到与输入的用户属性名称匹配的每个字段。

return (
  <div>
    {isLoading ? (
      <>
        {users &&
          users.map((user, index) => (
            <li style={{ listStyle: "none" }}>
              <CardHeader title="Update Profile" />
              <form onSubmit={handleSubmit(index)}> // <-- pass index
                <TextField
                  type="text"
                  value={user.firstName}
                  variant="outlined"
                  label="First Name"
                  name="firstName" // <-- add name attribute
                  fullWidth
                  onChange={changeHandler(index)} // <-- pass index to handler
                />
                ... other fields
                <Button type="submit">Submit</Button>
              </form>
            </li>
          ))}
      </>
    ) : (
      <h1>Loading...</h1>
    )}
  </div>
);

【讨论】:

  • 也许我们可以缩短为:users[index][name] = value; setUsers([...users])。而不是循环使用map
  • 放置 `onChange={setFirstName(index)}` 会导致错误提示“错误:重新渲染过多。React 会限制渲染次数以防止无限循环。”
  • @Mr_Green 为什么?您仍然至少迭代数据一次以复制它,并且您建议的方法会改变状态对象,这在 React 中是非常反模式的。如果您颠倒步骤并执行users[index] = { ...users[index], [name]: value },它将起作用。重要的部分是 users[index] 也是 React 的新对象引用。
  • @DrewReese 哦酷。不知道。谢谢。
  • @DrewReese 哦,好吧,没关系。谢谢
猜你喜欢
  • 2021-10-27
  • 2017-06-14
  • 2021-07-22
  • 2021-01-03
  • 1970-01-01
  • 1970-01-01
  • 2019-01-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多