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

2.0.0 • Public • Published

direct-react-router

NPM version Bundle size Build status License

Достоинства

  • redux first → не зависит от жизненного цикла компонентов → нет сайд-эффектов в компонентах (например, больше не нужна генерация действий в componentDidMount)
  • настройки роутинга в одном месте → в конфиге плоский список роутов и алиасы для них
  • алиасы вместо url → роутер парсит алиасы роутов в адресах и генерирует по алиасам правильные url → не нужно завязываться в коде на конкретные url (в т.ч. не нужны спец. компоненты для условного рендеринга)
  • можно использовать по частям (например, только middleware) → расширяемость (например, можно использовать с react-router, а можно и не использовать 😋)
  • поддержка TypeScript из коробки

Как установить

npm i direct-react-router

Зависимости: react ^16.8.6, redux ^4.0.1, react-redux ^7.0.0, history ^5.0.0.

Как использовать

Конфигурация

Конфигурация задает плоский список роутов и алиасы для них. Также вам понадобится экземпляр history.

import { createBrowserHistory } from 'history';
import { RouterConfig } from 'direct-react-router';
 
const history = createBrowserHistory();
 
const config: RouterConfig = {
    routes: {
        PAGE1: '/p1',
        PAGE2: '/p2/:login/count/:num'
    }
};

Синтакис шаблонов путей:

const config: RouterConfig = {
    routes: {
        EXAMPLE1: '/:foo/:bar',  // named parameters
        EXAMPLE2: '/:foo/:bar?', // optional parameters
        EXAMPLE3: '/:foo*',      // zero or more
        EXAMPLE4: '/:foo+',      // one or more
 
        // see details: https://npmjs.org/package/path-to-regexp
    }
};

При сравнении всегда exact === true, поэтому порядок описания роутов не важен. Если текущему URL соответствует несколько роутов, будет исключение.

Middleware

import { createRoutingMiddleware } from 'direct-react-router';
// ...
const routerMiddleware = createRoutingMiddleware(config, history);
// ...
const store = createStore(rootReducer, routerMiddleware);

Теперь при каждом изменении url будет генериироваться action, который вы можете обрабатывать любым нужным способом. В него приходит информация о новом URL и его параметрах + его alias в конфиге.

/*
{
    type: '@@direct-react-router/LOCATION_CHANGED',
    location: {
        key: '<route key>',
        pathname: '...',
        search: '...',
        hash: '...',
        params: { ... },
        query: { ... }
    },
    action: 'PUSH'
}
*/

Reducer + state

Вы можете подключить готовый редюсер, который будет обрабатывать события изменения URL и класть информацию в state. Также он отвечает за начальное состояние (начальный url).

import { RouterLocation, createRoutingReducer } from 'direct-react-router';
 
export interface State {
    location: RouterLocation;
    // ...
}
 
const rootReducer = combineReducers({
    location: createRoutingReducer(
        config,  // конфиг роутера
        history.location // начальный url
    ),
    // ...
});

Ссылки

import { Link } from 'direct-react-router';
// ...
 
render() {
    const href = '/p2/test/count/12?aa=1&bb=2#xxx';
    return <Link href={href}>page1</Link>;
}
 
/*
location: {
    pathname: '/p2/test/count/12',
    search: '?aa=1&bb=2',
    hash: '#xxx',
    key: 'PAGE2',
    params: { login: 'test', num: '12' },
    query: { aa: '1', bb: '2' }
}
*/
 

Генерация ссылок по ключам

import { AdvancedLink, RouterContext } from 'direct-react-router';
// ...
 
render() {
    return (
        <Provider store={store}>
            <RouterContext.Provider value={{ config }}>
                ...
                <AdvancedLink
                    routeKey='PAGE2'
                    params={{ login: 'test', num: '12' }}
                    query={{ aa: '1', bb: '2' }}
                    hash='#xxx'
                >
                    page2
                </AdvancedLink>
                ...
            </RouterContext.Provider>
        </Provider>
    );
}
 
/*
href:
    /p2/test/count/12?aa=1&bb=2#xxx
location: {
    pathname: '/p2/test/count/12',
    search: '?aa=1&bb=2',
    hash: '#xxx',
    key: 'PAGE2',
    params: { login: 'test', num: '12' },
    query: { aa: '1', bb: '2' }
}
*/
 

Внимание! проверьте, что нет лишних перерисовок

History options

Через пропсы компонентов Link и AdvancedLink можно настраивать параметры обращения к history api:

  • replace?: boolean - использовать REPLACE (по умолчанию PUSH)
  • state?: object | null - объект состояния, ассоциированный с новой записью истории браузера
  • forceReload?: boolean - перезагружать страницу при переходе по ссылке

Base path

Везде работаем с относительными путями.

  • Для адресной строки — указать basename в createBrowserHistory.
  • Для генерации ссылок — пробросить basename через контекст.
import { Link, RouterContext } from 'direct-react-router';
// ...
const basename = 'your/base/path';
 
// учитываем basename при обработке url
const history = createBrowserHistory({ basename });
 
 
render() {
    return (
        // учитываем basename при генерации url для ссылок
        <RouterContext.Provider value={{ basename }}>
            ...
            <Link href='/test/xxx' />
            ...
        </RouterContext.Provider>
    );
}
 
/*
href:
    /your/base/path/test/xxx
location: {
    pathname: '/test/xxx',
    ...
}
*/

Генерировать action при открытии страницы

Middleware генерирует экшены при изменении url в адресной строке. При открытии страницы экшен с текущим url по умолчанию не генерируется. Если он вам нужен, сгенерируйте его руками.

import { parseLocation, changeLocation } from 'direct-react-router';
// ...
 
const routerLocation: RouterLocation = parseLocation(config, history.location);
store.dispatch(changeLocation(routerLocation));

todo

  • импорт компонента ссылки из корня
  • устанавливать начальный path
  • location по умолчанию
  • редюсер
  • генерация ссылок по ключу????? (откуда брать конфиг? как вариант, можно коннектить каждую ссылку к стору и складывать конфиг в стор конфиг передается через контекст)
  • обрубать ? в query string
  • приоритет роутов - задаем в виде массива
  • придумать, как задавать query string и hash для AdvancedLink
  • exact
  • base path
  • атрибуты ссылки
  • callHistoryMethod, который принимает RouteArgs + добавить параметр с названием методов
  • выключать spa переходы через пропсы
  • persistQuery
  • переделать базовый компонент на HOC
  • query-string options

подумать

  • проверить, как ведет себя звездочка в роутах
  • проверить, что приходит в action, если адрес - url encoded
  • подумать, нужно ли кодировать hash при генерации url
  • когда происходит отписка от событий history

License

MIT

Readme

Keywords

none

Package Sidebar

Install

npm i direct-react-router

Weekly Downloads

0

Version

2.0.0

License

MIT

Unpacked Size

32 kB

Total Files

23

Last publish

Collaborators

  • dima117