Part 74: Enhancing React Applications with React Query: Using useMutation for POST Requests

[Pages] React Query

[Pages] React Query

React Query is a powerful library that simplifies data fetching and state management in React applications. In this blog post, we will explore how to use the useMutation hook to handle POST requests effectively. This approach not only cleans up your code but also provides built-in state management for loading and error states.

From useQuery to useMutation

Previously, we discussed how to use the useQuery hook to fetch and cache data, such as user information in a navigation bar. However, not all HTTP requests are GET requests. In cases where you need to send data, like when logging in, you'll want to use the useMutation hook instead.

Understanding useMutation

The useMutation hook in React Query is designed for executing actions that modify or "mutate" data, such as POST, PUT, or DELETE requests. Unlike useQuery, which fetches data on component mount, useMutation allows you to define a request upfront and execute it later, for example, when a user submits a form.

Refactoring the Sign-In Page with useMutation

Let's see how we can refactor a sign-in page to use useMutation, simplifying the code and improving state management:

// File: pages/sign-in.js

import { useRouter } from 'next/router';
import { useMutation } from 'react-query';
import Button from '../components/Button';
import Field from '../components/Field';
import Input from '../components/Input';

function SignInPage() {
  const router = useRouter();
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const mutation = useMutation(() => fetchJson('/api/login', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email, password }),
  }));

  const handleSubmit = async (event) => {
    event.preventDefault();
    try {
      const user = await mutation.mutateAsync();
      console.log('signed in:', user);
      router.push('/');
    } catch (err) {
      // mutation.isError will be true
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <Field label="Email">
        <Input
          type="email"
          value={email}
          onChange={(event) => setEmail(event.target.value)}
        />
      </Field>
      <Field label="Password">
        <Input
          type="password"
          value={password}
          onChange={(event) => setPassword(event.target.value)}
        />
      </Field>
      {mutation.isError && (
        <p className="text-red-700">
          Invalid credentials
        </p>
      )}
      {mutation.isLoading ? (
        <p>Loading...</p>
      ) : (
        <Button type="submit">Sign In</Button>
      )}
    </form>
  );
}

export default SignInPage;

Key Changes and Benefits

  1. Simplified State Management: By using useMutation, we replace manual state handling for loading and error states. The mutation object provides isLoading and isError flags that automatically update based on the request's status.

  2. Cleaner Code: The refactored code is cleaner and more readable, with the handleSubmit function focusing solely on form submission logic.

  3. Asynchronous Handling: The mutateAsync method allows us to handle the response data using await, making the code concise and easy to understand.

Testing the Changes

Once refactored, test your application to ensure that the sign-in functionality works correctly. Try signing in with incorrect and correct credentials to verify that the error and loading states are handled appropriately.

Conclusion

Using useMutation for POST requests in React Query not only simplifies your code but also enhances the user experience by providing built-in handling for loading and error states. This approach is particularly beneficial for actions that modify data, such as user authentication or form submissions. By adopting React Query's hooks, you can streamline your React applications and focus more on building features rather than managing state.

Last updated