Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/atomic-router/atomic-router

Platform-agnostic router that does not break your architecture
https://github.com/atomic-router/atomic-router

atomic-router effector forest javascript js router routing solid solidjs ts typescript

Last synced: about 3 hours ago
JSON representation

Platform-agnostic router that does not break your architecture

Awesome Lists containing this project

README

        

# Atomic Router

Simple routing implementation that provides abstraction layer instead of inline URL's and does not break your architecture

- Type-safe
- No inline URL's
- Atomic routes
- Does not break architecture
- Framework-agnostic
- Isomorphic (pass your own `history` instance and it works everywhere)

### Read the docs: [atomic-router.github.io](https://atomic-router.github.io)

> โ—๏ธ **Attention**: At the moment atomic-router team collecting issues and feature requests to improve current design. Upgrade atomic-router version with caution. We are going to write migration guide when/if the release will contain breaking changes. Thank you for reporting issues ๐Ÿงก

### Get view-library bindings

- โš›๏ธ [**React**](https://github.com/atomic-router/react)
- ๐Ÿƒ [**Forest**](https://github.com/atomic-router/forest)
- [**Solid**](https://github.com/atomic-router/solid)

## Installation

```bash
$ npm install effector atomic-router
```

## Initialization

Create your routes wherever you want:

```ts
// pages/home
import { createRoute } from 'atomic-router';
export const homeRoute = createRoute();

// pages/posts
import { createRoute } from 'atomic-router';
export const postsRoute = createRoute<{ postId: string }>();
```

And then create a router

```ts
// app/routing
import { createHistoryRouter } from 'atomic-router';
import { createBrowserHistory, createMemoryHistory } from 'history';
import { homeRoute } from '@/pages/home';
import { postsRoute } from '@/pages/posts';

const routes = [
{ path: '/', route: homeRoute },
{ path: '/posts', route: postsRoute },
];

const router = createHistoryRouter({
routes: routes,
});

// Attach history
const history = isSsr ? createMemoryHistory() : createBrowserHistory();
router.setHistory(history);
```

## Why atomic routes?

There are 3 purposes for using atomic routes:

- To abstract the application from hard-coded paths
- To provide you a declarative API for a comfortable work
- To avoid extra responsibility in app features

## Examples

Fetch post on page open

1. In your model, create effect and store which you'd like to trigger:

```tsx
export const getPostFx = createEffect<{ postId: string }, Post>(
({ postId }) => {
return api.get(`/posts/${postId}`);
}
);

export const $post = restore(getPostFx.doneData, null);
```

2. And just trigger it when `postPage.$params` change:

```tsx
//route.ts
import { createRoute } from 'atomic-router';
import { getPostFx } from './model';

const postPage = createRoute<{ postId: string }>();

sample({
source: postPage.$params,
filter: postPage.$isOpened,
target: getPostFx,
});
```

Avoid breaking architecture

Imagine that we have a good architecture, where our code can be presented as a dependency tree.
So, we don't make neither circular imports, nor they go backwards.
For example, we have `Card -> PostCard -> PostsList -> PostsPage` flow, where `PostsList` doesn't know about `PostsPage`, `PostCard` doesn't know about `PostsList` etc.

But now we need our `PostCard` to open `PostsPage` route.
And usually, we add extra responisbility by letting it know what the route is

```tsx
const PostCard = ({ id }) => {
const post = usePost(id);

return (

{post.title}
{post.title}
{/* NOOOO! */}

Read More


);
};
```

With `atomic-router`, you can create a "personal" route for this card:

```tsx
const readMoreRoute = createRoute<{ postId: id }>();
```

And then you can just give it the same path as your `PostsPage` has:

```tsx
const routes = [
{ path: '/posts/:postId', route: readMoreRoute },
{ path: '/posts/:postId', route: postsPageRoute },
];
```

Both will work perfectly fine as they are completely independent

## API Reference

```tsx
// Params is an object-type describing query params for your route
const route = createRoute();

// Stores
route.$isOpened; // Store
route.$params; // Store<{ [key]: string }>
route.$query; // Store<{ [key]: string }>

// Events (only watch 'em)
route.opened; // Event<{ params: RouteParams, query: RouteQuery }>
route.updated; // Event<{ params: RouteParams, query: RouteQuery }>
route.closed; // Event<{ params: RouteParams, query: RouteQuery }>

// Effects
route.open; // Effect
route.navigate; // Effect<{ params: RouteParams, query: RouteQuery }>

// Note: Store, Event and Effect is imported from 'effector' package
```