5. Advanced example

Now that we have seen a basic example it is time to show some more advanced examples.

Note: all examples below will use useQueryParams, but they are all possible with withQueryParams as well.

5.1 Handling changing query params

Here’s an example showing you how to trigger a change in query params.

The important parts are the queryChanged function which is called when the input element changes, and the pageChanged function which is called when going to the next page.

// Dashboard.tsx
import React from 'react';
import { useHistory, useLocation, useParams } from 'react-router';
import { Url, useQueryParams, urlBuilder } from '@42.nl/react-url';

type DashboardPathParams = {
  id: string;
}

type DashboardQueryParams = {
  page: number;
  query: string;
}

export function defaultDashboardQueryParams(): DashboardQueryParams {
  return {
    page: 1,
    query: '',
  };
}

type Props = {};

export default function Dashboard(props: Props) {
  const { id } = useParams<DashboardPathParams>();
  const location = useLocation();
  const history = useHistory();

  const queryParams = useQueryParams({
    location,
    defaultQueryParams: defaultDashboardQueryParams(),
    debugName: 'Dashboard',
  });

  /* 
    queryChanged re-navigates to the same url with a search query
    which restarts the page to 1.
  */
  function queryChanged(query: string) {
    history.push(
      /* 
        Keep the id as is in the path, but change the query params
        and change the search `query`. By not adding the `page` it
        will be reset to 1.
      */
      toDashboard({ id }, { query }),
    );
  }

  /* 
    pageChanged re-navigates to the same url with a changed page
    but with the same query as before
  */
  function pageChanged(page: number) {
    history.push(
      /* 
        Keep the id as is in the path, but change the page and
        keep the original search query. This is done by creating
        a copy from the old query params via the `...` operator.
      */
      toDashboard({ id }, { ...queryParams, page }),
    );
  }

  const { query, page } = queryParams;

  return (
    <div>
      <input type="text" value={query} onChange={event => queryChanged(event.target.value)} />

      <button onClick={() => pageChanged(page - 1)}>Prev</button>
      {page}
      <button onClick={() => pageChanged(page + 1)}>Next</button>
    </div>
  );
}

export const DASHBOARD_URL = '/dashboard/:id';

export function toDashboard(pathParams: DashboardPathParams, queryParams?: Partial<DashboardQueryParams>): Url {
  return urlBuilder({
    url: DASHBOARD_URL,
    pathParams,
    queryParams,
    defaultQueryParams: defaultDashboardQueryParams(),
  });
}

The trick is simple: whenever you want to change the query params simply call url function again.

5.2 Fetching data based on path variables and query params

This example show you how you can load data from the back-end based on the query params and path params. The trick here is to fetch data from the back-end each time the query params or path params change.

The example uses @42.nl/spring-connect to fetch the user from the back-end, and react-async to handle the loading state.

import React from 'react';
import { Url, urlBuilder, useQueryParams } from '@42.nl/react-url';
import { useAsync } from 'react-async';
import { useHistory, useLocation, useParams } from 'react-router';

/* User is a @42.nl/spring-connect Resource */
import User from 'User';

export type UserEditPathParams = {
  id: string;
}

export type UserEditQueryParams = {
  active: boolean;
}

export function defaultUserEditQueryParams(): UserEditQueryParams {
  return {
    active: false,
  };
}

type Props = {};

export function loadUser(
  { id, queryParams }: 
  { id: string; queryParams: UserEditQueryParams }
): Promise<User> {
  return User.one(id, queryParams);
}

export default function UserEdit(props: Props) {
  const { id } = useParams<DashboardPathParams>();
  const location = useLocation();

  const queryParams = useQueryParams({
    location,
    defaultQueryParams: defaultUserEditQueryParams(),
    debugName: 'UserEdit',
  });

  /* 
    Calls `loadUsers each time the query params or path params have
    changed. This is achieved by setting the `watch`.
  */
  const { data, isLoading } = useAsync(loadUser, { id, queryParams, watch: `${id}${queryParams.active}` });

  if (isLoading && !data) {
    return <h1>Loading...</h1>;
  }

  const user = data as User;

  return <h1>Hey you are editing: {user.name}</h1>;
}

export const USER_EDIT_URL = '/users/:id/edit';

export function toUserEdit(pathParams: UserEditPathParams, queryParams?: Partial<UserEditQueryParams>): Url {
  return urlBuilder({
    url: USER_EDIT_URL,
    pathParams,
    queryParams,
    defaultQueryParams: defaultUserEditQueryParams(),
  });
}