How do I put an initialValue taken from the useEffect in the textField?

Viewed 78

I have this edit page where the initial value of the first name, last name, and address should be from the data retrieved from the firebase firestore using the useEffect.

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

        setUsers(arr);
      });

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

handleSubmit to save the updated information in the 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);
    }
  };

Below is the entire code:

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;

Let's say I wanted to edit the address of user "John Park". Those text fields should have the initial value of the first and last names of John so even if the address field was the only one that was updated, the fields first name and last name won't be an empty string when saved in the firestore. How should I do this? Thank you.

1 Answers

It seems you are updating the local state, but it isn't initialized to anything but an empty string.

Your updates should be to the local copy of users you fetched, and the submit handler should grab a specific user to update in your backend.

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

Change handler - curried function to take index and return handler. It maps the previous users state to the next, updating the user at the specified index.

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

Submit handler - curried function to take index of specific user object to send to backend.

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

Pass the current mapped index to the handlers. Add the name attribute to each field that matches the user property name for the input.

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>
);
Related