Categories
React

How to Compare Old Values and New Values with the React useEffect Hook?

Sometimes, we may want to compare the old and new values of a state in our React component created with React hooks.

In this article, we’ll look at how to compare the old and new values of a state with components created with React hooks.

Store Old Values in a Ref

We can store old values in a ref since assigning values to them won’t trigger a re-rendering of the component but the value will persist after each render cycle.

Therefore, we can write:

import React, { useEffect, useRef, useState } from "react";

const usePrevious = (value) => {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
};

export default function App() {
  const [count, setCount] = useState(0);
  const prevCount = usePrevious(count);
  useEffect(() => {
    console.log("prevCount: ", prevCount, "count: ", count);
  }, [prevCount, count]);

  return (
    <div className="App">
      <button onClick={() => setCount((c) => c + 1)}>increment</button>
      <p>{count}</p>
    </div>
  );
}

to create the usePrevious hook to store the previous value of a state.

The hook takes the value parameter with the state or prop value we want to store.

We call useEffect with a callback to set the current property of the ref to store value in it.

We didn’t pass in a 2nd argument so the useEffect callback will run every render cycle.

And we return the value of ref.current so that we can assign it to a value in a component and use it.

In App , we create the count state with the useState hook.

And we call the usePrevious hook with count to store the previous value of count in the ref ,

Below that, we watch the values of prevCount and count with the useEffect hook by passing in an array with prevCount and count as the 2nd argument.

Then below that, we have a button that calls setCount when we click it to update count .

And then we show the count .

In the console log, we should see that prevCount should have the previous value of count .

Conclusion

We can store the old value of a state or prop by storing the value in a ref so we can use it in our component.

Categories
React

How to Call a Function with React useEffect Only Once When the Component Mounts?

In many situations, we want to run the useEffect callback only when the component mounts.

In this article, we’ll look at how to call the useEffect callback only when the component mounts.

Pass in an Empty Array into the useEffect Hook

To run the useEffect hook callback only once when the component mounts, we just have to pass in an empty array into the 2nd argument of useEffect hook.

For instance, we can write:

import React, { useEffect } from "react";

export default function App() {
  useEffect(() => {
    console.log("mounted");
  }, []);

  return <div className="App"></div>;
}

We just pass in an empty array into useEffect and the callback would only run once.

So we only see 'mounted' logged when the component is mounted.

We can also move the useEffect hook call into its own function if we use it in multiple places:

import React, { useEffect } from "react";

const useMountEffect = (fun) => useEffect(fun, []);

export default function App() {
  useMountEffect(() => {
    console.log("mounted");
  });

  return <div className="App"></div>;
}

We create the useMountEffect hook that takes the fun function and call useEffect in the function the same way we did before.

Run Code When a Component Unmounts

To run code when a component unmounts, all we have to do is to return a function in the useEffect hook.

For instance, we can write:

import React, { useEffect } from "react";

export default function App() {
  useEffect(() => {
    console.log("mounted");

    return () => {
      console.log("unmounted");
    };
  }, []);

  return <div className="App"></div>;
}

We return a function that logs 'unmounted' .

We’ll see it run when we unmount the component.

Conclusion

To run a function only once when a React component mounts, we just have to pass in an empty array as the 2nd argument of the useEffect hook.

Categories
React

How to Make the React useState Hook Setter Function Reflect Changes Immediately?

Sometimes, we may see that the state changes we made with the useState hook’s state setter function doesn’t reflect at the time we expect them to.

In this article, we’ll look at how to make the React useState hook’s state setter function gets reflected when we want it.

Using the useState State Setter Function Properly

To make sure we pick the latest changes to a state after a usetState state setter function is run, we should watch the latest value of the state with the useEffect hook.

For instance, we can write:

import React, { useEffect, useState } from "react";

export default function App() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log(count);
  }, [count]);

  return (
    <div className="App">
      <button onClick={() => setCount((c) => c + 1)}>increment</button>
      <p>{count}</p>
    </div>
  );
}

We call the useState hook to return the setCount function to set the state.

And we call setCount with a callback that takes the previous state value and return the new state value based on that.

The useEffect callback runs when count ‘s value is changed.

So we can see the latest value of count logged in the useEffect callback.

When we click on the increment button, we show the value of count .

We’ve to use the useEffect hook to watch the state value because React’s useState state setter function is async.

The array we pass in as the 2nd argument of useEffect has the state or prop values we want to watch.

Therefore, we can’t get the value of count right after calling the setCount function.

The state setter function will trigger a re-render when it’s called.

And the latest value of count will be available after the next render.

Conclusion

To get the latest value of a state and do something with it, we’ve to use the useEffect hook with a callback and the array of values we want to watch.

Then in the useEffect callback, we get the latest value of whatever we’re watching.

Categories
React

Making HTTP Requests with React Query — Async Mutations and Invalidate Requests

The React Query library lets us make HTTP requests easily in our React apps.

In this article, we’ll look at how to make HTTP requests with React Query.

mutateAsync

The mutateAsync method lets us call mutate is an async manner.

It returns a promise which lets us commit our mutation request in an async manner, which doesn’t hold up the JavaScript main thread.

For instance, we can write:

import axios from "axios";
import React, { useState } from "react";
import { useMutation } from "react-query";

export default function App() {
  const { reset, mutateAsync } = useMutation((data) =>
    axios.post("https://jsonplaceholder.typicode.com/posts", data)
  );
  const [title, setTitle] = useState("");

  const onCreateTodo = async (e) => {
    e.preventDefault();
    try {
      const todo = await mutateAsync({
        title
      });
      console.log(todo);
    } catch (error) {
      console.log(error);
    } finally {
      console.log("done");
    }
  };

  return (
    <div>
      <form onSubmit={onCreateTodo}>
        <input
          type="text"
          value={title}
          onChange={(e) => setTitle(e.target.value)}
        />
        <br />
        <button type="submit">Create Todo</button>
        <button type="button" onClick={() => reset()}>
          reset
        </button>
      </form>
    </div>
  );
}

We call mutateAsync which returns a promise with the response data from the axios.post call.

Retry Mutations

With React Query, we can easily retry our mutation HTTP request if it returns an error.

We just have to set the retry option to the number of times we want to retry.

For instance, we can write:

import axios from "axios";
import React, { useState } from "react";
import { useMutation } from "react-query";

export default function App() {
  const { reset, mutateAsync } = useMutation(
    (data) => axios.post("https://jsonplaceholder.typicode.com/posts", data),
    {
      retry: 3
    }
  );
  const [title, setTitle] = useState("");

  const onCreateTodo = async (e) => {
    e.preventDefault();
    try {
      const todo = await mutateAsync({
        title
      });
      console.log(todo);
    } catch (error) {
      console.log(error);
    } finally {
      console.log("done");
    }
  };

  return (
    <div>
      <form onSubmit={onCreateTodo}>
        <input
          type="text"
          value={title}
          onChange={(e) => setTitle(e.target.value)}
        />
        <br />
        <button type="submit">Create Todo</button>
        <button type="button" onClick={() => reset()}>
          reset
        </button>
      </form>
    </div>
  );
}

We call the useMutation hook with an object that has the retry property set to 3 to retry up to 3 times if the mutation request fails.

Invalidate Queries

We can invalidate queries so we can mark a query request as stale and make the request again automatically.

For instance, we can write:

index.js

import { StrictMode } from "react";
import ReactDOM from "react-dom";
import { QueryClient, QueryClientProvider } from "react-query";
import App from "./App";

const queryClient = new QueryClient();
queryClient.invalidateQueries("yesNo", { exact: true });

const rootElement = document.getElementById("root");
ReactDOM.render(
  <QueryClientProvider client={queryClient}>
    <StrictMode>
      <App />
    </StrictMode>
  </QueryClientProvider>,
  rootElement
);

App.js

import axios from "axios";
import React from "react";
import { useQuery } from "react-query";
export default function App() {
  const { data } = useQuery("yesNo", () => axios("https://yesno.wtf/api"));

  return <div>{JSON.stringify(data)}</div>;
}

We call:

queryClient.invalidateQueries("yesNo", { exact: true });

to invalidate the query by the key.

exact set to true means the key of the query request must match exactly before it’s invalidated.

Conclusion

We run mutation requests asynchronously and invalidate query requests to make the request again with Reacr Query.

Categories
React

Making HTTP Requests with React Query — Mutation Side Effects

The React Query library lets us make HTTP requests easily in our React apps.

In this article, we’ll look at how to make HTTP requests with React Query.

Mutation Side Effects

We can watch for events that are emitted when mutations are being committed.

For instance, we can write:

import axios from "axios";
import React, { useState } from "react";
import { useMutation } from "react-query";

export default function App() {
  const { reset, mutate } = useMutation(
    (data) => axios.post("https://jsonplaceholder.typicode.com/posts", data),
    {
      onMutate: (variables) => {
        console.log(variables);
        return {};
      },
      onError: (error, variables, context) => {
        console.log(error, variables, context);
      },
      onSuccess: (data, variables, context) => {
        console.log(data, variables, context);
      },
      onSettled: (data, error, variables, context) => {
        console.log(data, error, variables, context);
      }
    }
  );
  const [title, setTitle] = useState("");

  const onCreateTodo = (e) => {
    e.preventDefault();
    mutate({
      title
    });
  };

  return (
    <div>
      <form onSubmit={onCreateTodo}>
        <input
          type="text"
          value={title}
          onChange={(e) => setTitle(e.target.value)}
        />
        <br />
        <button type="submit">Create Todo</button>
        <button type="button" onClick={() => reset()}>
          reset
        </button>
      </form>
    </div>
  );
}

The onMutate method is run when the mutation request is being made.

variables has the mutation data from the data parameter.

onError is run when there’s an error with the mutation.

error has the error object.

variables is the same as before.

context has the context data which has the mutation request data.

onSuccess is run when the mutation request is successful.

data has the mutation response data.

variables and context are the same as the other callback parameters.

onSettled is run whenever a mutation request is finished regardless of whether it’s successful or not.

All the parameters are the same as before.

We can also add the same callbacks to the mutate method call.

For instance, we can write:

import axios from "axios";
import React, { useState } from "react";
import { useMutation } from "react-query";

export default function App() {
  const { reset, mutate } = useMutation(
    (data) => axios.post("https://jsonplaceholder.typicode.com/posts", data),
    {
      onMutate: (variables) => {
        console.log(variables);
        return {};
      },
      onError: (error, variables, context) => {
        console.log(error, variables, context);
      },
      onSuccess: (data, variables, context) => {
        console.log(data, variables, context);
      },
      onSettled: (data, error, variables, context) => {
        console.log(data, error, variables, context);
      }
    }
  );
  const [title, setTitle] = useState("");

  const onCreateTodo = (e) => {
    e.preventDefault();
    mutate(
      {
        title
      },
      {
        onMutate: (variables) => {
          console.log(variables);
          return {};
        },
        onError: (error, variables, context) => {
          console.log(error, variables, context);
        },
        onSuccess: (data, variables, context) => {
          console.log(data, variables, context);
        },
        onSettled: (data, error, variables, context) => {
          console.log(data, error, variables, context);
        }
      }
    );
  };

  return (
    <div>
      <form onSubmit={onCreateTodo}>
        <input
          type="text"
          value={title}
          onChange={(e) => setTitle(e.target.value)}
        />
        <br />
        <button type="submit">Create Todo</button>
        <button type="button" onClick={() => reset()}>
          reset
        </button>
      </form>
    </div>
  );
}

The callbacks we add to the object we pass in as the 2nd argument of mutate will run after the callbacks we added to the useMutation hook.

Conclusion

We can add callbacks to the object we pass into the useMutation hook or mutate method to watch for any events that are triggered when making our mutation request with React Query.