bradstiff / react-app-location

A package to avoid repetition with Routes and URLs, and reduce boilerplate with location param parsing in React Apps
MIT License
97 stars 8 forks source link

Make it independent of react-router? #1

Closed IniZio closed 5 years ago

IniZio commented 5 years ago

Currently toLink / toRoute will always convert to react-router components, but some frameworks like Nextjs use their own routing components.

This library is underrated and I feel like it can in fact be made language-agnostic?

stevenpetryk commented 5 years ago

Interesting idea. I haven't used this library, but it may be a matter of defining a "link generator".

const generateLink = ({ to, ...rest }) => {
  // Return whatever you want
  return <MyCustomLink href={to} {...rest} />
})

const ArticleLocation = new Location('/articles/:id', { id: Yup.number() /* ... */ }, { generateLink });

const ArticleLink = ArticleLocation.toLink('Article 1', { id: 1 })
// => <MyCustomLink href='/articles/1' />

Of course it's a little bit verbose to specify this on every Location, so some global configuration would probably be better.

It might be nice if there was a separate Location just for sane react-router defaults, maybe like:

import ReactRouterLocation from 'react-app-location/react-router-location'

To allow tree shaking to eliminate react-router for non-react-router apps.

stevenpetryk commented 5 years ago

Actually, when I think about it more, it could be a matter of simply hiding toLink and toRoute between another package (toUrl is already agnostic). To mimic the example from the README:

  import React from "react";
  import { Link, BrowserRouter, Switch, Route } from 'react-router-dom';
  import * as Yup from 'yup';
  import Location from "react-app-location";
+ import { toRoute, toLink } from "react-app-location/react-router";

  const HomeLocation = new Location('/');
  const ArticleLocation = new Location('/articles/:id', { id: Yup.number().integer().positive().required() });

  const App = () => (
      <BrowserRouter>
          <Switch>
              {/* Regular Route */}
              <Route path={HomeLocation.path} component={Home} exact />
              {/* Route with params automatically passed as props to your component */}
-             {ArticleLocation.toRoute({ component: Article, invalid: NotFound }, true)}
+             {toRoute(ArticleLocation, { component: Article, invalid: NotFound }, true)}
              <Route component={NotFound} />
          </Switch>
      </BrowserRouter>
  );

  const Home = () => (
      <div>
          <header>Articles</header>
          <ul>
              {/* <Link to={'/articles/1'}>Article 1</Link> */}
-             <li>{ArticleLocation.toLink('Article 1', {id: 1})}</li>
+             <li>{toLink(ArticleLocation, 'Article 1', {id: 1})}</li>
              {/* <Link to={'/articles/2'}>Article 2</Link> */} 
-             <li>{ArticleLocation.toLink('Article 2', {id: 2})}</li> 
+             <li>{toLink(ArticleLocation, 'Article 2', {id: 2})}</li> 
              {/* Also works */}
              <li><Link to={ArticleLocation.toUrl({id: 3})}>Article 3</Link></li>  
              {/* Clicking results in <NotFound /> */}
              <li><Link to={ArticleLocation.toUrl({id: 'oops-not-an-int'})}>Article 4</Link></li>  
              {/* Also results in <NotFound /> */}
              <li><Link to={'/articles/oops-not-an-int'}>Article 5</Link></li>  
          </ul>
      </div>
  );

  //id has been parsed from the URL, cast to int, and merged into props
  const Article = ({id}) => <header>`Article ${id}`</header>;

  const NotFound = () => (
      <div>
          <header>Page not found</header>
          <p>Looks like you have followed a broken link or entered a URL that does not exist on this site.</p>
      </div>
  );
bradstiff commented 5 years ago

I'm for it.

toUrl and parseLocationParams would be moved to a base (router-agnostic) package, and toRoute and toLink would be remain in the package that depends on react-router.

But I also want to provide a way to provide automatic location param injection in the base package (currently only available via toRoute). My current thought is to build a withLocationParams HOC that would take an App Location definition, parse the parameters from the URL, and pass them as props.

What do you guys think of that?

IniZio commented 5 years ago

Would be nice, maybe could use a pub/sub mechanism though so that it is easier to do same thing in other frameworks like Vue.js?

bradstiff commented 5 years ago

I built app-location, which is router-agnostic and framework-agnostic. And I changed react-app-location to extend app-location by providing integration with React Router 4.

IniZio commented 5 years ago

Since the package has been released, guess this issue can be closed? Thanks for the code :wink: