npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-router-query-hooks

v2.2.0

Published

React Router hooks with query string parsing

Downloads

254

Readme

React Router Hooks With Query String Parsing

Test

A small package that augments the basic react-router-dom hooks (useLocation and useHistory) to be more query string aware by parsing using the query-string library.

Primarily, it exports a simple useQueryParams hook for reading and manipulating the URL query string (useful for UIs where sort order, or page number is encoded in the URL). See the usage notes for more details.

Depends on:

  • query-string
  • Peer Dependency react-router-dom@^5.1 or greater (needs router hooks)

This package is also written in ES6, so to use, you'll need some transpiler such as babel to run on node_modules.

How to Install

$ npm install react-router-query-hooks react-router-dom

or

$ yarn add react-router-query-hooks react-router-dom

Basic Usage

There are two main uses for this package:

  1. The higher-level useQueryParams hook.
  2. The lower-level replacements for useLocation or useHistory: useLocationWithQuery and useHistoryWithQuery.

Note: With both usages, you'll need to have your components wrapped in a Router (any flavor exported by React Router should work).

1. The useQueryParams Hook

The easiest way to use this library is to use the higher-level useQueryParams hook. It's useful for reading or updating the current query parameters from the URL. Behind the scenes, it's basically a sweetened version of the useHistoryWithQuery hook outlined in the next section.

import { useQueryParams } from "react-router-query-hooks";

const MyComponentInRouter = () => {
  const [query, { pushQuery, replaceQuery }] = useQueryParams();
};

The return value for the useQueryParams hook is similar to React's useState; however it includes two setters instead of one: pushQuery and replaceQuery—to either add a history entry or replace the current history entry respectively. In the above example the first entry in the tuple (query) contains the parsed query parameters from the URL.

2. Augmented useLocation and useHistory hooks

If you want to use some more primitive hooks, the useQueryParams hook builds upon the two hooks that wrap the underlying React Router hooks (useLocation, and useHistory).

useLocationWithQuery

This modified hook adds the query key to React Router's location object which contains a parsed version of location.search. As with useLocation it is read-only (has no setters).

import { useLocation } from "react-router-dom";
import { useLocationWithQuery } from "react-router-query-hooks";

const MyComponentInRouter = () => {
  // const location = useLocation();
  const location = useLocationWithQuery(); // Same interface as above but with location.query

  /* Augmented location object:
  {
    key: 'ac3df4', // not with HashHistory!
    pathname: '/somewhere',
    search: '?some=search-string',
    query: {
      some: "search-string"  // <- ADDED PROPERTY
    },
    hash: '#howdy',
    state: {
      [userDefined]: true
    }
  }
  */
};

useHistoryWithQuery

This modified hook builds upon React Router's history object and the above location additions:

  • In history.location, it adds the query key to React Router's location object (as above)
  • Furthermore, it supports URL updates with the query key. So history.replace supports both paths (as before) and location objects with the query key.
  • GOTCHA: React router does NOT trigger an update to history.location when the location changes. While the internal history object is updated, this is a mutation, and React will not update the component. You must listen to the location separately using useLocationWithQuery or the combined useQueryParams to update the component upon a location change.
import { useHistory } from "react-router-dom";
import { useLocationWithQuery } from "react-router-query-hooks";

const MyComponentInRouter = () => {
  // const history = useHistory();
  const history = useHistoryWithQuery();

  // Supports push & replace with query object in location (along with supporting the existing API):
  return (
    <>
      <button onClick={() => history.replace({ query: { page: 1 } })}>
        Go to Page 1
      </button>
      <button onClick={() => history.push({ query: { page: 1 } })}>
        Go to Page 1
      </button>
    </>
  );
};

Library Details

  • None of the hooks shadow the URL state—everything is read from the URL.

Fuller Example

The motivation for this package was to have an easy way to maintain URL query parameters, that was simple, somewhat light, and used the URL as the source of truth. Furthermore, it was designed to support UIs that have pagination:

import React from "react";
import { useQueryParams } from "react-router-query-hooks";

const MyComponentInRouter = () => {
  const [query, { replaceQuery }] = useQueryParams();
  const { page, sortBy, order } = query;

  // Useful for calling APIs with a page and sort order
  const { data } = useAPI("/my-resource", { page, sortBy, order });

  return (
    <div>
      <Table
        data={data}
        onHeaderClick={(sortBy, order) =>
          replaceQuery({ ...query, sortBy, order })
        }
      />
      <Pagination onClick={page => replaceQuery({ ...query, page })} />
    </div>
  );
};

Optimization

Sometimes you don't want to recreate a callback on every render. Consider the example above: on each render, onHeaderClick callback is redefined. The next optimization (if needed) is to wrap that callback in useCallback:

import React, { useCallback } from "react";
import { useQueryParams } from "react-router-query-hooks";

const MyComponentInRouter = () => {
  const [query, { replaceQuery }] = useQueryParams();
  const { page, sortBy, order } = query;

  // Useful for calling APIs with a page and sort order
  const { data } = useAPI("/my-resource", { page, sortBy, order });
  const onClick = useCallback(
    (sortBy, order) => replaceQuery({ ...query, sortBy, order }),
    [query]
  );

  return (
    <div>
      <Table data={data} onHeaderClick={onClick} />
      <Pagination onClick={page => replaceQuery({ ...query, page })} />
    </div>
  );
};

That's a good start: on each render we'll use the same onClick callback and only redefine if query changes. In some cases, query may change frequently. The next optimization we can make is to update using a function (this should look familiar when using useState):

const onClick = useCallback(
  (sortBy, order) =>
    replaceQuery(currentQuery => ({ ...currentQuery, sortBy, order })),
  []
);

Now, our callback doesn't have any dependencies, and will be reused for each render! This method of changing the state works for both replaceQuery and pushQuery.