Home > Enterprise >  Updating a hook without calling the set updater method in React
Updating a hook without calling the set updater method in React

Time:11-18

What would happen if a react hook's state is updated directly instead of calling the set updater function.

const MyComponent = () => {
    const [formData, setFormData] = useState({
        gender: "",
        dob: "",
        contact: "",
        institute: "",
        institute_other: "",
        occupation: "",
        occupation_other: "",
        street_address: "",
        city: "",
        division: "",
        country: "",
        email: "",
        user_name: "",
    });

  const [username, setUserName] = useState("");
  const [email, setEmail] = useState("");
}

This is the hook that I have initialized

In an input handler function I am updating a certain element in the state variable object directly like this --

// ... My other codes

formData.division = "some_Division";
formData.city = "some_city";

// ... Rest of the code

This happens to update my state object. But I definitely understand that this is not a good practice, but can I get a clarification about how it breaks the rule of hooks in react and what problems it might pose in the future (if it may).

N.B: I have tried to update the state in this way inside the input handler function but with no luck!

const handleInputChange = (e) => {
    switch (e.currentTarget.name) {
      case "country":
        setUpdateDivision(true);
        setUpdateCity(true);
        setDivisionHook("");
        setCityHook("");
        setFormData((formData) => ({ ...formData, division: "", city: "" }));
        setDivisionOptions([]);
        setCityOptions([]);
        console.log(e.currentTarget.selected);
        break;
      case "division":
        setUpdateCity(true);
        setCityHook("");
        setFormData((formData) => ({ ...formData, city: "" }));
        setCityOptions([]);
        break;
      case "institute":
        setUpdateInstitute(true);
        break;
      case "occupation":
        setUpdateOccupation(true);
        break;

      default:
        break;
    }

    setFormData({
      ...formData,
      [e.currentTarget.name]: e.currentTarget.value,
      email: email,
      user_name: username,
    });
  };

It does not update the state variable.

Edit: I have added the complete handleInputChange function for clarity.

CodePudding user response:

The issue with updating the state directly is that is a mutation and won't trigger React to rerender a component with the updated state value. It's not breaking the "Rules of Hooks", it's breaking React's rule against state/props mutations.

See Using State Correctly

Do Not Modify State Directly

For example, this will not re-render a component:

// Wrong
state.comment = 'Hello';
Instead, use setState():

// Correct
setState({comment: 'Hello'});

I suspect your enqueued state updates aren't working due to a stale enclosure. Using a functional state update resolves this by updating from the previous state versus the state closed over in scope. This also helps if you are enqueueing several state updates within the same enclosure where each update needs to update from the previous result.

setFormData(formData => ({
  ...formData,                                   // shallow copy previous state
  [e.currentTarget.name]: e.currentTarget.value, // update property
}));

Update

Another issue is that enqueued React state updates are not immediate. They are enqueued (think of this as a request to React to "please update state X with value Y at your earliest convenience"), and batch processed asynchronously. When you update with setFormData several times and don't use a functional update then each update overwrites the previously enqueued update when it's processed using the current state value of the enclosure over what is passed in the updater function.

  • Related