Categories
React

How to commit side effects in React Query mutations?

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

In this article, we’ll look at how to commit side effects in React Query mutations?

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.

Categories
React

How to make mutations with React Query?

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

In this article, we’ll look at how to make mutations with React Query?

Mutations

Mutations let us make HTTP requests to change data on a server by creating, updating, and deleting them.

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

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 { useMutation } from "react-query";

export default function App() {
  const { mutate, isLoading, isError, isSuccess } = useMutation((data) =>
    axios.post("https://jsonplaceholder.typicode.com/posts", data)
  );

  return (
    <div>
      <div>{isLoading && "loading"}</div>
      <div>{isError && "error"}</div>
      <div>{isSuccess && "success"}</div>
      <button
        onClick={() => {
          mutate({
            title: "foo",
            body: "bar",
            userId: 1
          });
        }}
      >
        Add Todo
      </button>
    </div>
  );
}

We call the useMutation hook with a callback that lets us make a POST request to the API to submit some data.

The callback should return a promise.

The request payload is stored in the payload parameter.

The hook returns the mutate function that lets us make the request.

isLoading is true when the request is loading.

isError is true when a request fails.

isSuccess is true when the request is successfully completed.

Alternatively, we can replace isLoading , isSuccess , and isError with the status property:

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

export default function App() {
  const { mutate, status } = useMutation((data) =>
    axios.post("https://jsonplaceholder.typicode.com/posts", data)
  );

  return (
    <div>
      <div>{status === "loading" && "loading"}</div>
      <div>{status === "error" && "error"}</div>
      <div>{status === "success" && "success"}</div>
      <button
        onClick={() => {
          mutate({
            title: "foo",
            body: "bar",
            userId: 1
          });
        }}
      >
        Add Todo
      </button>
    </div>
  );
}

'loading' status is the status when the request is loading.

'error' status is the status when the request has an error.

'success' status is the status when the request is successful.

Resetting Mutation State

We can clear the mutation request state after the request is done.

To do this, we can call the reset method:

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

We call the reset method when we click on the reset the state returned by the useMutation hook.

Conclusion

We can make requests that change data on the server with the React Query useMutation hook.

Categories
React

How to make parallel queries with React Query?

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

In this article, we’ll look at how to make parallel queries with React Query?

Parallel Queries

Sometimes, we may want to make multiple GET requests concurrently.

To do this, we can add multiple useQuery hooks:

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

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({
    queryKey: ["todo", 1],
    queryFn: ({ queryKey: [, id] }) => {
      return axios(`https://jsonplaceholder.typicode.com/posts/${id}`);
    }
  });
  const { data: yesNoData } = useQuery("yesNo", () =>
    axios("https://yesno.wtf/api")
  );

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

We have useQuery hooks to make 2 requests in parallel.

Dynamic Parallel Queries with useQueries Hook

We can add dynamic parallel queries with the useQueries hook.

It returns an array of query results.

For instance, we can write:

import axios from "axios";
import React from "react";
import { useQueries } from "react-query";
export default function App() {
  const todos = useQueries(
    Array(5)
      .fill()
      .map((_, i) => i + 1)
      .map((id) => {
        return {
          queryKey: ["todo", id],
          queryFn: ({ queryKey: [, id] }) => {
            return axios(`https://jsonplaceholder.typicode.com/posts/${id}`);
          }
        };
      })
  );

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

We call useQueries with an array of query objects.

We call map to map the array of numbers we created with:

Array(5)
  .fill()
  .map((_, i) => i + 1)

to query objects.

And todos is an array of query objects.

Dependent Queries

We can make also make multiple HTTP requests where one depends on the other.

To do this, we write:

import axios from "axios";
import React from "react";
import { useQuery } from "react-query";
export default function App() {
  const { data: id } = useQuery("id", () => Promise.resolve(1));

  const { data } = useQuery(
    ["todo", id],
    ({ queryKey: [, id] }) => {
      return axios(`https://jsonplaceholder.typicode.com/posts/${id}`);
    },
    {
      enabled: Boolean(id)
    }
  );

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

to make one query after the other.

We call the first useQuery hook to get an id for our todo.

Then we call useQuery again to pass in the id to the callback.

We set the enabled property to a boolean expression to indicate when we want to query to run.

We set it so that the 2nd query is run when id is truthy, which should only happen when we get the id from the first useQuery hook.

Conclusion

We can make parallel or sequential queries with React Query easily.

Categories
React

How to make queries with React Query?

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

In this article, we’ll look at how to to make queries with React Query

How to make queries with React Query?

Query functions are functions that returns a promise and it’s passed into the 2nd argument of the useQuery hook.

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

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

to return the promise return by the axios function.

The returned promises’ data will be set as the value of the data property.

Handling and Throwing Errors

React Query expects that an error is thrown in the query function when an error occurred.

If it doesn’t then we’ve to throw it ourselves.

For instance, we can write:

import React from "react";
import { useQuery } from "react-query";
export default function App() {
  const { error } = useQuery("todo", async () => {
    const response = await fetch(
      "https://jsonplaceholder.typicode.com/posts/100000000000"
    );
    if (!response.ok) {
      throw new Error("Network response was not ok");
    }
    return response.json();
  });

  return <div>{error && error.message}</div>;
}

If we use the Fetch API to make HTTP requests, then we’ve to check whether the response.ok property is true .

If it’s not, then we’ve to throw an error to let React Query know that an error has occurred.

This has to be done since fetch doesn’t throw an error when we get a non-200 series response.

If response.ok is true , we return the response by calling response.json() .

Query Object

We can pass in a query object instead of pass in every as separate arguments to make our GET request.

For instance, we can write:

import axios from "axios";
import React from "react";
import { useQuery } from "react-query";
export default function App() {
  const { data } = useQuery({
    queryKey: ["todo", 1],
    queryFn: ({ queryKey: [, id] }) => {
      return axios(`https://jsonplaceholder.typicode.com/posts/${id}`);
    }
  });

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

queryKey has the identifier of the request.

queryFn is the function for making the request.

Conclusion

We can pass in functions that returns a promise to make requests to the useQuery hook to make GET requests with React.

Categories
JavaScript Answers

How to Set a DOM element as the First Child with JavaScript?

Sometimes, we may want to set a DOM element as the first child element with JavaScript.

In this article, we’ll look at ways to set a DOM element as the first child with JavaScript.

Using the prepend Method

In modern browsers, an HTML element object comes with the prepend method to let us prepend an element as the first child.

For instance, if we have the following HTML:

<div>  
  <p>  
    bar  
  </p>  
  <p>  
    baz  
  </p>  
</div>

Then we can write the following JavaScript to prepend an element as the first child of the div:

const div = document.querySelector('div')  
const newChild = document.createElement('p')  
newChild.textContent = 'foo'  
div.prepend(newChild)

We get the div with the document.querySelector method.

Then we call document.createElement to create an element.

Next, we set the textContent property to add some content to the p element we created.

And then we call div.prepend to prepend newChild as the first child of the div.

There’s also the append method to append an element as the last child of a parent element.

Using the insertAdjacentElement Method

We can also use the insertAdjacentElement method to insert an element as the first child of an element.

For instance, if we have the same HTML:

<div>  
  <p>  
    bar  
  </p>  
  <p>  
    baz  
  </p>  
</div>

Then we can write:

const div = document.querySelector('div')  
const newChild = document.createElement('p')  
newChild.textContent = 'foo'  
div.insertAdjacentElement('afterbegin', newChild)

to call insertAdjacentElement with 'afterbegin' and the newChild to prepend newChild as the first child element of the div.

Other possible values for the first argument of insertAdjacentElement is:

  • beforebegin: before the element itself.
  • beforeend: just inside the element, after its last child.
  • afterend: after the element itself.

Using the insertBefore Method

Another method we can use to prepend an element as the first child is to use the insertBefore method.

For instance, we can write:

const div = document.querySelector('div')  
const newChild = document.createElement('p')  
newChild.textContent = 'foo'  
if (div.firstChild) {  
  div.insertBefore(newChild, div.firstChild);  
} else {  
  div.appendChild(childNode);  
}

given the same HTML we have in the previous examples to prepend newChild as the first child of the div.

We check if there’s a firstChild element attached to the div.

If there is, then we call insertBefore to insert newChild before the firstChild of the div.

Otherwise, we just call appendChild to append newChild as a child element.

Conclusion

We can use various HTML element methods to prepend an element as the first child of a parent element.