An alternative to handle state in React: the URL !
Tags: react, javascript, router, state
Published: 2018-06-01 08:31:52

---
title: An alternative to handle state in React: the URL !
published: true
description: React + React-Router + URL + state management = <3
tags: #react #javascript #router #state
---

For a french version of this article, go [here](https://www.apprendre-react.fr/tutorial/library/state/Supprimer-Redux-en-utilisant-l-url-de-votre-application/) :)

# The state of a react app

Managing the global state of a React app is an endless war between libraries. Therefore, in order to add my 2 cents, we'll see how to do the exact same thing using the URL and react-router.

# URL FTW

In single page application, the URL does not really matter. Most of the time, it is just an endpoint to request all the assets and that's it.

Landing on `https://myApp.io` ou `https://myApp.io?user=gael&job=dev` would have no impact on what you'll see on the first loading of the app. 

Let's change that. 


# Some code

I started to use develop this idea in a *side project* ([code](https://github.com/GaelS/first-contrib-app) and [demo](https://first-contrib.surge.sh) to play with :))

However, for this article, I have redeveloped a [codesandbox](https://codesandbox.io/s/5v0v7vwo6k) to focus only on what matters for here.

First of all, how will we use the URL ? 

Well, we'll use everything located after the **?** in the URL, the so-called *search params*.

> Some docs [here](https://developer.mozilla.org/en-US/docs/Web/API/URL/searchParams) on the subject.

</br>
</br>

## Get values from the URL

In the context of this article, we are going to look for only one parameter that we'll name *query*.

To retrieve that parameter (if it is indeed in the URL eg. `https://myApp.io?query=javascript` ), we are going to check the ... *search params*. Luckily, they are easy to find, located in the object `window`. More precisely in `winndow.location.search`.

Therefore, if we request: `www.first-contrib?query=react`, what we get in the console is: 

`console.log(window.location.search); // "?query=react"`

Ideally, rather than a `string`, it would be more convenient to manipulate a JS object properly formatted. To achieve that, instead of splitting the URL by `=` and `?`, we'll use the `URLSearchParams` object, available in recent browsers. Otherwise, you can polyfill it thanks to this [lib](https://www.npmjs.com/package/url-search-params-polyfill) for instance.

With code, it yields to :

``` javascript
function getParams(location) {
  const searchParams = new URLSearchParams(location.search);
  return {
    query: searchParams.get('query') || '',
  };
}
```

therefore, 

``` javascript
const params = getParams('www.first-contrib.fr?query=react');

console.log(params) // { query: "react" }

```
Now that we can get an object out of the URL, we'll make it work with our app which uses **react-router**. We'll therefore create a `router` which handles a `route` getting the properties in the URL as `props`.


``` javascript
import React from "react";
import { render } from "react-dom";
import { BrowserRouter as Router, Route } from "react-router-dom";

// ...
// getParams code above

//a simple component to display 
//the value of the query ...
// which is for now unknown
//so we'll instantiate it with an empty value
const MainPage = (props) => {
  let query = '';
  return (
    <h2>{`Query : ${query}`}</h2>
  );
}

const App = () => (
  <React.Fragment>
    <Router>
      <React.Fragment>
        <Route path="/" component={MainPage} />
      </React.Fragment>
    </Router>
  </React.Fragment>
);

render(<App />, document.getElementById("root"));

```
To get the actual value of `query`, we'll have to manipulate the function `getParams` that we wrote as well as the *implicit* `props` that `MainPage` receives from the `Route` object : `<Route path="/" component={MainPage} />`

If we logged that `props`, we'd get :

`{match: Object, location: Object, history: Object, /*other stuff */}`

What is of interest here is the `location` object similar to the `window.location` we manipulated earlier. Therefore, we can update the `MainPage` component to get the value inside the URL:

``` javascript
const MainPage = (props) => {
  const { location } = props;
  const { query } = getParams(location);
  
  return (
      <h2>{`My query: ${query}`}</h2>
  );
}
```
Now `MainPage` is plugged to the URL !

</br>
</br>

## Update the URL (and the state) ! 

Now that we can read from the URL, we are going to implement a way to update the URL accordingly to the state of our app.

For that, a simple `input` will do:

``` javascript
class InputPage extends React.Component {
  
  state = { inputValue: "" };

  updateInputValue = e => this.setState({ inputValue: e.target.value });
  
  render() {
    return (
      <React.Fragment>
        <input
          type="text"
          placeholder="Change your URL !"
          value={this.state.inputValue}
          onChange={this.updateInputValue}
        />
        <input type="button" value="Change the URL" onClick={null} />
      </React.Fragment> 
    );
  }
}
```
So far, our component manage an internal state to display its current value but we still have to implement the `onClick` function to update the URL with that very same value.

We saw that the implicit `props` object from `Route` looks like that:

`{match: Object, location:Object, history: Object, /*d'autres valeurs */}`

What is of interest here is the `history` (additional information about `history` [here](https://reacttraining.com/react-router/web/api/history)...)

Thanks to its `push` function which according to **React Router** documentation :

> Pushes a new entry onto the history stack

To say it simply, `push` will allow us to update the URL ! 

So if the `query` value in our input is `javascript`, we'll have to update our URL to the following value: `www.myApp.io?query=javascript`. We therefore need to generate the new `searchParams` of our URL. In order to do that `URLSearchParams` object will help us once again:

``` javascript
function setParams({ query = ""}) {
  const searchParams = new URLSearchParams();
  searchParams.set("query", query);
  return searchParams.toString();
}
```

> Mind that without a default value for query when query is actually `undefined`, the generated URL would be `?query=undefined`...
  
Now we can write :

``` javascript
const url = setParams({ query: "javascript" });
console.log(url); // "query=javascript"

```
 We can then implement the `onClick` in he input component.

 ``` javascript
class InputPage extends React.Component {
  state = { inputValue: "" };
  updateInputValue = e => this.setState({ inputValue: e.target.value });
  
  updateURL = () => {
    const url = setParams({ query: this.state.inputValue });
    //do not forget the "?" !
    this.props.history.push(`?${url}`);
  };

  render() {
    return (
      <React.Fragment>
          <input
          type="text"
          className="input"
          placeholder="What am I looking for ?"
          value={this.state.inputValue}
          onChange={this.updateInputValue}
        />
        <input
          type="button"
          className="button"
          value="Update the URL !"
          onClick={this.updateURL}
        />
      </React.Fragment> 
    );
  }
}
```

Now if we change that value of the input, a click on the button we'll trigger the update of the URL and the `MainPage` will display the new value accordingly !

One of the nice thing that comes by having the state of your app in a URL is when you copy/paste link. Given that the state is included in this URL, we'll find the app in a specific state on first load ! 

When you deal with a search engine for instance, you can trigger the query as soon as the app is loaded. In this [application](https://github.com/GaelS/first-contrib-app),  I use [react-apollo](https://github.com/apollographql/react-apollo) but in naive way, we can implement the same thing with any HTTP client.

Let's create a component which triggers request using [axios](https://github.com/axios/axios) and the Github REST API (which does not require any authentication) as soon as it gets `props` using some of its lifecycle methods. 

``` javascript

const httpClient = axios.create({
  baseURL: "https://api.github.com"
});

class ResultsPage extends React.Component {
  state = { results: [], loading: false, error: false };

  //Search as soon as it is mounted !!
  componentDidMount() {
    return this.searchRepositories(this.props.query);
  }

  //Search as soon as query value is updated
  componentWillReceiveProps(nextProps) {

    if (nextProps.query !== this.props.query) {
      this.setState({ query: nextProps.query });
      return this.searchRepositories(nextProps.query);
    }
  }

  searchRepositories = query => {
    
    //handle if query is undefined
    if (!query) {
      return this.setState({
        results: []
      });
    }

    this.setState({ loading: true, error: false });
    
    //the actual search on Github
    return httpClient
      .get(`/search/repositories?q=${query}`)
      .then(({ data }) =>
        this.setState({
          results: data.items,
          loading: false
        })
      )
      .catch(e => this.setState({ loading: false, error: true }));
  };

  
  render() {
    return (
      <div>
        {this.state.results.map(repo => (
          <div key={repo.id}>
            <a href={repo.html_url}>
              {repo.name}
            </a>
            <div>{`by ${repo.owner.login}`}</div>
          </div>
        ))}
      </div>
    );
  }
}

```

That's it! We now have a component which triggers request whenever the `query` params contained in the URL is updated !


As mentionned earlier, you can find a live example [here](https://codesandbox.io/s/5v0v7vwo6k).

In our example, we deal with only one `searchParams` but it becomes really cool and powerful if more components can modify the URL. For instance, pagination, filtering, sorting, etc can become parameters of the URL. It could then look like that: `https://myApp.io?query=react&sort=ASC&filter=issues&page=2`.

The code would be similar as what we did previously. By modifying the URL, implicit `props` provided by the `Route` component are updated. It then triggers a rerender and update all the children listening to a specific value in the URL. Therefore it leads to UI update(s) or side effect(s) such as an HTTP call.

</br>
</br>
</br>
</br>


# Conclusion

That's it ! This article was an attempt to show that there are still alternatives to handle the global state of a React App that can be very light in terms of bundle size (0kb in modern browser ><) ans still fun, simple and providing for free a (kind of) deep linking effect which I find very cool :)

Hope you enjoy !