react-router-parsed
TypeScript icon, indicating that this package has built-in type declarations

2.1.0 • Public • Published

react-router-parsed

CircleCI Coverage Status semantic-release Commitizen friendly npm version

This package provides a wrapper to handle url parameter and querystring parsing and error handling in an organized fashion.

Rationale

After working with react-router 4 enough, I started to realize that I had a lot of duplicated code to parse my URL params and query string within render methods and event handlers for my components. For instance:

class EditDeviceView extends React.Component {
  render() {
    const {match: {params}} = this.props
    const organizationId = parseInt(params.organizationId)
    const deviceId = parseInt(params.deviceId)

    return (
      <Query variables={{organizationId, deviceId}}>
        {({loading, data}) => (
          <form onSubmit={this.handleSubmit}>
            ...
          </form>
        )}
      </Query>
    )
  }
  handleSubmit = () => {
    // duplicated code:
    const {match: {params}} = this.props
    const organizationId = parseInt(params.organizationId)
    const deviceId = parseInt(params.deviceId)

    ...
  }
}

After awhile, I had had enough of this. While I could have moved the parsing logic to a function in the same file, I realized everything would be easier if I parse the params and query outside of my component and pass in the already-parsed values as props.

Quick Start

npm install --save react-router react-router-parsed
import Route from 'react-router-parsed/Route'
import useRouteMatch from 'react-router-parsed/useRouteMatch'

Parsing URL parameters

If you need to parse any url parameters, add a paramParsers property and consume the params prop in your route component, render function, or children:

import Route from 'react-router-parsed/Route'

const EditUserRoute = () => (
  <Route
    path="/users/:userId"
    paramParsers={{ userId: parseInt }}
    render={({ params: { userId }, ...props }) => (
      <EditUserView {...props} userId={userId} />
    )}
  />
)
import useRouteMatch from 'react-router-parsed/useRouteMatch'

const EditUserRoute = () => {
  const {
    match,
    params: { userId },
    error,
  } = useRouteMatch({
    path: '/users/:userId',
    paramParsers: { userId: parseInt },
  })

  if (!match) return null
  if (error) return <ErrorAlert>{error.message}</ErrorAlert>
  return <EditUserView match={match} userId={userId} />
}

For each property in paramParsers, the key is the url parameter name, and the value is a function that takes the following arguments and returns the parsed value.

  • raw - the raw string value of the parameter
  • param - the key, or parameter name
  • info - a hash of additional info; right now, just {match}

Parsing location.search

If you need to parse location.search, add a queryParser property and consume the query prop in your route component, render function, or children:

import qs from 'qs'
import Route from 'react-router-parsed/Route'

const EditUserRoute = () => (
  <Route
    path="/"
    queryParser={(search) => qs.parse(search.substring(1))}
    render={({ query: { showMenu }, ...props }) => (
      <App {...props} showMenu={showMenu} />
    )}
  />
)

Error handling

If any of your parsers throws errors, they will be collected and passed to an (optional) renderErrors function:

import Route from 'react-router-parsed/Route'

const EditUserRoute = () => (
  <Route
    path="/users/:userId"
    paramParsers={{
      userId: (userId) => {
        const result = parseInt(userId)
        if (!userId || !userId.trim() || !Number.isFinite(result)) {
          throw new Error(`invalid userId: ${userId}`)
        }
        return result
      },
    }}
    render={({ params: { userId }, ...props }) => (
      <EditUserView {...props} userId={userId} />
    )}
    renderErrors={({ paramParseErrors }) => (
      <div className="alert alert-danger">
        Invalid URL: {paramParseErrors.userId}
      </div>
    )}
  />
)

renderErrors will be called with the same props as render, plus:

  • paramParseError - a compound Error from parsing params, if any
  • paramParseErrors - an object with Errors thrown by the corresponding paramParsers
  • queryParseError - the Error from queryParser, if any
  • error - paramParseError || queryParseError

With the useRouteMatch hook, error paramParseError, paramParseErrors, queryParseError are props of the returned object:

const EditUserRoute = (): React.Node | null => {
  const {
    match,
    params: { userId },
    paramParseErrors,
  } = useRouteMatch({
    path: '/users/:userId',
    paramParsers: {
      userId: (userId) => {
        const result = parseInt(userId)
        if (!userId || !userId.trim() || !Number.isFinite(result)) {
          throw new Error(`invalid userId: ${userId}`)
        }
        return result
      },
    },
  })
  if (paramParseErrors) {
    return (
      <div className="alert alert-danger">
        Invalid URL: {paramParseErrors.userId}
      </div>
    )
  }
  if (!match) return null
  return <EditUserView match={match} userId={userId} />
}

Readme

Keywords

Package Sidebar

Install

npm i react-router-parsed

Weekly Downloads

390

Version

2.1.0

License

MIT

Unpacked Size

26.7 kB

Total Files

12

Last publish

Collaborators

  • jedwards1211