React URL Query
Using React Hooks? Check out use-query-params for managing URL query parameters via hooks.
Update 2019-09-13 use-query-params has been updated to have a HOC and a render props solution. I recommend using that library for any modern query param problems as it is more likely to be updated.
A library for managing state through query parameters in the URL in React. It integrates well with React Router and Redux and provides additional tools specifically targeted at serializing and deserializing state in URL query parameters. With React URL Query, you can create components where there is no difference in handling state from an external store like Redux and state from the URL.
Motivation
When developing web applications, it's really common to want to encode parts of the state in URL query parameters to capture what users are seeing on screen. Think of things like filters, toggles, selected items, and so on. Storing them in the URL allows users to easily link others to what they are seeing and facilitates discussion. React URL Query makes doing this really easy.
The current set of tools for React does not provide facilities for easy interaction with query parameters.
The fantastic library React Router is the standard for integrating URL changes in React applications and provides us access to the query parameters as an object, but the fields within are always represented as strings-- just as they appear in the URL. This makes sense, but means developers must work to decode the strings into the proper types depending on what is being stored in a given param (e.g. numbers, booleans, arrays, objects). Furthermore, when one wants to update the query parameters in the URL, React Router gives us functions to do so via context.router
, but no facilities for encoding our values as strings.
The current front-runner for state management in React app's is Redux. When first dealing with encoding state in URL query parameters, it's common to wonder how to get them in sync with what is in the Redux store. Dan Abramov, Redux's creator, suggests that you don't do that. Instead, he puts forth the idea of decoding the query parameters in the mapStateToProps
function (you can look into props.location.query
if you're using React Router). However, when it comes to encoding changes to the query parameters back into the URL, we're back to calling router functions directly.
React URL Query is based off the idea that we can have an equivalent to mapStateToProps
and mapDispatchToProps
(see Redux's Usage with React) but for the URL. In fact, it allows you to provide mapUrlToProps
and mapUrlChangeHandlersToProps
when configuring components to do just that. You can decode URL query params into props in mapUrlToProps
and you can encode them back into params through change handlers in mapUrlChangeHandlersToProps
. This means the component code itself doesn't need to be aware of which props are managed by Redux and which props are managed by the URL-- it can just read props and call change handlers, and the application will update appropriately.
There are some very common patterns when encoding and decoding URL query parameters: each parameter has a type and a name in the URL and parameters of a given type should always be encoded or decoded the same way. It's cumbersome and repetitive to always create the map___ToProps
functions to handle this common use case, so React URL Query provides a succinct way of describing URL query parameters through its urlPropsQueryConfig
. You can just describe the type and names of the query parameters, and the decoded query params along with their change handlers will be passed in as props to the wrapped component.
Check it out below or in the other examples to see how it works.
Installation
npm install --save react-url-query
How do I use it?
A number of examples have been created demonstrating a variety of methods of using the library with different technologies. Here is the most basic form of using it in a component:
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { addUrlProps, UrlQueryParamTypes } from 'react-url-query';
/**
* Specify how the URL gets decoded here. This is an object that takes the prop
* name as a key, and a query param specifier as the value. The query param
* specifier can have a `type`, indicating how to decode the value from the
* URL, and a `queryParam` field that indicates which key in the query
* parameters should be read (this defaults to the prop name if not provided).
*
* Here we specify two props, `bar` and `foo` that correspond to query parameters
* `bar` and `fooInUrl` respectively. React URL Query will interpret URLs like
* /app?bar=react&fooInUrl=137 and pass the props `{ bar: "react", foo: 137 }`
* to the MainPage component.
*/
const urlPropsQueryConfig = {
bar: { type: UrlQueryParamTypes.string },
foo: { type: UrlQueryParamTypes.number, queryParam: 'fooInUrl' },
};
class MainPage extends PureComponent {
static propTypes = {
// URL props are automatically decoded and passed in based on the config
bar: PropTypes.string,
foo: PropTypes.number,
// change handlers are automatically generated when given a config.
// By default they update that single query parameter and maintain existing
// values in the other parameters.
onChangeFoo: PropTypes.func,
onChangeBar: PropTypes.func,
}
static defaultProps = {
foo: 123,
bar: 'bar',
}
render() {
const { foo, bar, onChangeFoo, onChangeBar } = this.props;
return (
<div>
<div>
foo={foo}
<button onClick={() => onChangeFoo(999)}>Set foo to 999</button>
</div>
<div>
bar={bar}
<button onClick={() => onChangeBar('testing')}>
Set bar to "testing"
</button>
</div>
</div>
);
}
}
/**
* Use the addUrlProps higher-order component to hook-in react-url-query.
*/
export default addUrlProps({ urlPropsQueryConfig })(MainPage);
If you prefer, instead of using a urlPropsQueryConfig
you can provide the functions mapUrlToProps
and mapUrlChangeHandlersToProps
, as shown in the basic-mapUrlToProps example.
You'll also need to configure which history
to use, typically done wherever you initialize your application. Examples of doing this with different setups are shown in the examples section.
If you are using react-router, how you link in the history depends on the version.
React Router v2
import { configureUrlQuery } from 'react-url-query';
import { browserHistory } from 'react-router'
configureUrlQuery({ history: browserHistory });
React Router v4
import { RouterToUrlQuery } from 'react-url-query';
import Router from 'react-router/BrowserRouter';
ReactDOM.render(
<Router>
<RouterToUrlQuery>
<App />
</RouterToUrlQuery>
</Router>,
document.getElementById('root')
);
React Router v5
import { RouterToUrlQuery } from 'react-url-query';
import {
BrowserRouter as Router,
__RouterContext as RouterContext
} from 'react-router-dom';
ReactDOM.render(
<Router>
<RouterToUrlQuery routerContext={RouterContext}>
<App />
</RouterToUrlQuery>
</Router>,
document.getElementById('root')
);
Not using React Router. If you're not using react-router, you'll need to instantiate the history yourself manually:
import { configureUrlQuery } from 'react-url-query';
import createHistory from 'history/createBrowserHistory';
const history = createHistory();
configureUrlQuery({ history });
Examples
- basic - Basic usage of React URL Query without any third party tools
- basic-mapUrlToProps - Basic usage of React URL Query without any third party tools, uses alternative approach of
mapUrlToProps
instead ofurlPropsQueryConfig
. - redux - Integration with Redux
- redux-with-actions - Alternative integration with Redux using dispatched actions
- react-router-v2-and-redux - Integration with React Router v2 and Redux
- react-router-v4-and-redux - Integration with React Router v4 and Redux
Development
During development of examples, it can be helpful to have a watch running automatically rebuilding the package when changes take place. To get this running run:
npm run dev
Building
npm run build
Linting
npm run lint
To lint examples, run:
npm run lint:examples
Testing
npm run test
To test examples, run:
npm run test:examples
Working on docs
When editing the docs, it helps to have a dev server watching changes. To do this, run:
npm run docs:watch
To build the docs, run:
npm run docs:build
To publish the docs, run:
npm run docs:publish
License
MIT