@ackee/chris 中文文档教程

发布于 4年前 浏览 39 项目主页 更新于 3年前

ackee|chris

GitHub license CI Status PRs Welcome Dependency Status

Chris

对你的 redux - react-router 应用程序有用的补充。

包裹名称指的是圣克里斯托弗,他们路线上旅行者的守护神。 Chris 只是其较短的形式,用于更容易记忆和书写。

Table of contents

Installation

使用 npm:

npm i -s @ackee/chris

使用 yarn:

yarn add @ackee/chris

API

Selectors

routingSelector

connected-react-router 状态,返回 位置


Sagas

runRouteDependencies(handlers: {[ActionType], sagaHandler}, selector: ReduxSelector)

通过使用 runRouteDependenciesrouting-history,您可以从 React componentDidMount< 中排除业务逻辑/code> 和 componentWillUnmount 并在 React 组件之外下载当前页面所需的数据。

示例 - 对新路线

import { runRouteDependencies } from '@ackee/chris';

const handlers = {
    '/user/:id': function* ({ id }) {
        // fetch user data and store it
    }
};

export default function* () {
    yield all([
        takeEvery(LOCATION_CHANGE, runRouteDependencies, handlers),
    ])
}

反应

import { runRouteDependencies } from '@ackee/chris';
import routingHistory from '@ackee/chris/routing-history';

const { activeLocationSelectorFactory, previousLocationSelectorFactory } = routingHistory;

// 'history' is the name of routingHistory module state
const activeLocationSelector = activeLocationSelectorFactory('history'); 
const previousLocationSelector = previousLocationSelectorFactory('history');

const handlers = {
    '/user/:id': function* ({ id }) {
        // fetch user data and store it
    }
};
const postHandlers = {
    '/user/:id': function* ({ id }) {
        // flush user data from the store
    }
};

export default function* () {
    yield all([
        takeEvery(LOCATION_CHANGE, runRouteDependencies, handlers, activeLocationSelector),
        takeEvery(LOCATION_CHANGE, runRouteDependencies, postHandlers, previousLocationSelector),
    ])
}

runRouteActions(handlers: {[ActionType], sagaHandler})

作出

routeRefresh(initType: ActionType, type: ActionType, handlers: function)

用于刷新路由依赖项的 Saga。 每次调度 initType 操作时,它都会运行 runRouteDependencies(handlers)。 在调用 runRouteDependencies 之前,还会分派带有 type 的操作。

假设我们有一个包含更多页面的应用程序,这些页面包含带分页的项目列表。 每次页面更改时我们都想 为特定页面加载新数据。

export default function* () {
    yield all([
        routeRefresh(
            actionTypes.app.SET_PAGE_START,
            actionTypes.app.SET_PAGE,
            {
                '/users': getUsers,
                '/invoices': getInvoices,
                ...
            },
        ),
    ])
}

当用户在应用程序操作中更改查看列表的页面时,应该调度 SET_PAGE_START 而不是 SET_PAGE。 然后 SET_PAGE_START 被捕获到 routeRefresh 中,接下来会

  • action SET_PAGE is dispatched with all properties (except the type of course) from SET_PAGE_START. The SET_PAGE action should set the page to the state.
  • as a next step, route dependencies are procceded with this call js runRouteDependencies({ '/users': getUsers, '/invoices': getInvoices, ... })

runSagas(sagas: {[ActionType]: sagaHandler})

自动为给定事件调用所有给定的 sagas。 saga 的调用被 try-catch saga 包裹起来,它会派发相关的动作。 看例子更好理解

function*() getData {
    // This is a pseudo code, implementation of getting data from the API 
    // and setting them back to the state is up to you
    const users = yield api.get(config.api.users);
    yield put(setUsers(users));
}

export default function*() {
    return yield runSagas({
        [actionTypes.designs.REQUEST_USERS]: getData,
        [actionTypes.designs.REQUEST_OTHER_DATA]: getOtherData,
        ...
    });
}
  • Once REQUEST_USERS is dispatched in application it's caught and handled by getData handler.
  • When getData saga
    • throw an error during its run, action REQUEST_USERS_FAILED with error property is dispatched
    • run without error then action REQUEST_USERS_SUCCEEDED with property result is dispatched, where result is anything that getData saga returns (nothing in our example ????)
  • Action REQUEST_USERS_COMPLETED is dispatched at the end every time, no matter if running getData failed or succeded

小魔法解释:
在 saga 处理期间分派的操作会在 runSagas 帮助程序中自动创建,作为初始操作(在我们的示例中为 REQUEST_USERS)和其中一个前缀 - 的组合_FAILED_SUCCEEDED_COMPLETED


Modules

Routing history

redux&中有一个路由历史模块,用于处理历史。 react-router 应用调用 routingHistory

import routingHistory from '@ackee/chris/routing-history';

Utilities

combineDependenciesHandlers(...routeHandlers) => combinedRouteHandlers

Helper 来组合 runRouteDependecies 的依赖处理程序。 接受无限数量的处理程序对象 ({'template': sagaHandler}) 并返回一个用于 runRouteDependecies 的使用。 在处理程序对象中支持相同的键

Usage
import { routeHandlers as usersHandlers } from 'Modules/users';
import { routeHandlers as reviewsHandlers } from 'Modules/reviews';

export const appHandlers = {
    '/': homeSaga,
    '/logout': function* () {
        // ...
    },
};

const combinedRouteHandlers = combineDependenciesHandlers(
    appHandlers,
    usersHandlers,
    reviewsHandlers
);

runRouteDependencies(combinedRouteHandlers);

每个模块(例如 Modules/users)可以导出它自己的 routeHandlers 对象和 combineDependenciesHandlers 实用程序处理它们的合并。


HOC

fetchDependencies(config?: Config) => (Component) => ComponentThatFetchDependencies

用于请求包装组件数据的高阶组件。 如果您使用 HOC 包装页面组件,它将确保在组件呈现后立即请求所需的数据。

默认情况下,HOC 调用 fetch 函数,在组件挂载时通过 props 传递,并在组件卸载前调用 clear。 如果它不适合您的需要,请提供定义您自己的处理方式的配置。 配置中的所有方法都是可选的,如果未提供,则使用默认方法。

interface Config {
    onLoad?: (props) => void;
    onUnload?: (props) => void;
    shouldReFetch?: (prevProps, nextProps) => boolean;
}
Example - Use with default config
const UsersListPageContainer = compose(
    connect(
        state => ({ users: state.users }),
        dispatch => bindActionCreators({ 
            fetch: requestUsers,
            clear: deleteUsers 
        }, dispatch),
    ),
    fetchDependencies(),
)(UsersListPage);

const App = () => (
    <div>
        <UserListPageContainer />
    </div>
);
Example - Use with custom config
const UserDetailPageContainer = compose(
    connect(
        (state: State) => ({
            user: state.user,
            userId: state.selectedUserId
        }),
        dispatch => bindActionCreators({
            requestUserDetail: requestUser,
            clearUserDetail: clearUser,
        }, dispatch),
    ),
    fetchDependencies({
        onLoad: ({ userId, requestUserDetail }) => {
            requestUserDetail(userId);
        },
        onUnload: ({ clearUserDetail }) => {
            clearUserDetail();
        },
        shouldReFetch: (prevProps, props) => {
            return prevProps.userId !=== props.userId;
        },
    }),
)(UserDetailPage);

const App = () => (
    <div>
        <UserListPageContainer />
    </div>
);

routeDependencies(config?: Config) => (Component) => ComponentWithRouteDependencies

Alert 使用 HOC 的重要先决条件是您必须拥有 react-router你的应用程序。

HOC 与 fetchDependencies 具有相同的目的和工作方式,几乎没有例外。

  • It wraps component also with withRouter HOC so all props receive also router's match, location and history objects.
  • Its config is slightly different especially in method names. Look at the definition: typescript interface Config { onRouteEnter?: (props) => void; onRouteLeave?: (props) => void; shouldReRoute?: (prevProps, nextProps) => boolean; }
  • Default implementation of shouldReRoute invoke refetch of data every time any part of url change. It's used until you provide your own.
Example - Use with default config
const UsersListPageContainer = compose(
    connect(
        state => ({ users: state.users }),
        dispatch => bindActionCreators({ 
            fetch: requestUsers,
            clear: deleteUsers 
        }, dispatch),
    ),
    routeDependencies(),
)(UsersListPage);

const App = () => (
    <Router>
        <div>
            <Route path="/users" component={UserListPageContainer}/>
        </div>
    </Router>
);
Example - Use with custom config
const UserDetailPageContainer = compose(
    connect(
        (state: State) => ({ user: state.user }),
        dispatch => bindActionCreators({
            requestUserDetail: requestUser,
            clearUserDetail: clearUser,
        }, dispatch),
    ),
    routeDependencies({
        onRouteEnter: ({ match, requestUserDetail }) => {
            requestUserDetail(match.params.id);
        },
        onRouteLeave: ({ match, clearUserDetail }) => {
            clearUserDetail();
        },
        shouldReRoute: (prevProps, props) => {
            return prevProps.match.params.id !=== props.match.params.id;
        },
    }),
)(UserDetailPage);

const App = () => (
    <Router>
        <div>
            <Route path="/users/:id" component={UserDetailPageContainer}/>
        </div>
    </Router>
);

ackee|chris

GitHub license CI Status PRs Welcome Dependency Status

Chris

Useful additions for your redux - react-router based app.

Name of package refers to Saint Christopher, patron of travellers at their routes. Chris is just its shorter form used for easier remembrance and writability.

Table of contents

Installation

Using npm:

npm i -s @ackee/chris

Using yarn:

yarn add @ackee/chris

API

Selectors

routingSelector

Selector for connected-react-router state, which returns location.


Sagas

runRouteDependencies(handlers: {[ActionType], sagaHandler}, selector: ReduxSelector)

With usage of runRouteDependencies and routing-history, you can exclude business logic from React componentDidMount and componentWillUnmount and download the data you need for the current page outside of React components.

Example - react to new route

import { runRouteDependencies } from '@ackee/chris';

const handlers = {
    '/user/:id': function* ({ id }) {
        // fetch user data and store it
    }
};

export default function* () {
    yield all([
        takeEvery(LOCATION_CHANGE, runRouteDependencies, handlers),
    ])
}

Example - react to new route and clean data when leaving it

import { runRouteDependencies } from '@ackee/chris';
import routingHistory from '@ackee/chris/routing-history';

const { activeLocationSelectorFactory, previousLocationSelectorFactory } = routingHistory;

// 'history' is the name of routingHistory module state
const activeLocationSelector = activeLocationSelectorFactory('history'); 
const previousLocationSelector = previousLocationSelectorFactory('history');

const handlers = {
    '/user/:id': function* ({ id }) {
        // fetch user data and store it
    }
};
const postHandlers = {
    '/user/:id': function* ({ id }) {
        // flush user data from the store
    }
};

export default function* () {
    yield all([
        takeEvery(LOCATION_CHANGE, runRouteDependencies, handlers, activeLocationSelector),
        takeEvery(LOCATION_CHANGE, runRouteDependencies, postHandlers, previousLocationSelector),
    ])
}

runRouteActions(handlers: {[ActionType], sagaHandler})

Alias for runRouteDependencies saga.

routeRefresh(initType: ActionType, type: ActionType, handlers: function)

Saga to refresh route dependecies. It run runRouteDependencies(handlers) every time initType action is dispatched. Also action with type is dispatched before runRouteDependencies is invoked.

Let's say we have an application with more pages that have list of items with paging. Every time page change we want to load new data for particular page.

export default function* () {
    yield all([
        routeRefresh(
            actionTypes.app.SET_PAGE_START,
            actionTypes.app.SET_PAGE,
            {
                '/users': getUsers,
                '/invoices': getInvoices,
                ...
            },
        ),
    ])
}

When user change page of viewed list in application action SET_PAGE_START instead of SET_PAGE should be dispatched. Then SET_PAGE_START is caught in routeRefresh and following happen

  • action SET_PAGE is dispatched with all properties (except the type of course) from SET_PAGE_START. The SET_PAGE action should set the page to the state.
  • as a next step, route dependencies are procceded with this call js runRouteDependencies({ '/users': getUsers, '/invoices': getInvoices, ... })

runSagas(sagas: {[ActionType]: sagaHandler})

Automatically invokes all given sagas for given event. The invoke of saga is wrapped with try-catch saga that dispatch relevant actions. Look at the example fo better undestanding

function*() getData {
    // This is a pseudo code, implementation of getting data from the API 
    // and setting them back to the state is up to you
    const users = yield api.get(config.api.users);
    yield put(setUsers(users));
}

export default function*() {
    return yield runSagas({
        [actionTypes.designs.REQUEST_USERS]: getData,
        [actionTypes.designs.REQUEST_OTHER_DATA]: getOtherData,
        ...
    });
}
  • Once REQUEST_USERS is dispatched in application it's caught and handled by getData handler.
  • When getData saga
    • throw an error during its run, action REQUEST_USERS_FAILED with error property is dispatched
    • run without error then action REQUEST_USERS_SUCCEEDED with property result is dispatched, where result is anything that getData saga returns (nothing in our example ????)
  • Action REQUEST_USERS_COMPLETED is dispatched at the end every time, no matter if running getData failed or succeded

Little magic explanation:
Actions dispatched during processing of a saga are automatically created inside the runSagas helper as a composition of the initial action (REQUEST_USERS in our example) and one of the prefixes - _FAILED, _SUCCEEDED or _COMPLETED.


Modules

Routing history

There is a routing history module for handling history in redux & react-router apps called routingHistory

import routingHistory from '@ackee/chris/routing-history';

Utilities

combineDependenciesHandlers(...routeHandlers) => combinedRouteHandlers

Helper to combine dependencies handlers for runRouteDependecies. Accepts infinite number of handlers objects ({'template': sagaHandler}) and returns exactly one for usage in runRouteDependecies. Supports same keys in the handlers objects

Usage
import { routeHandlers as usersHandlers } from 'Modules/users';
import { routeHandlers as reviewsHandlers } from 'Modules/reviews';

export const appHandlers = {
    '/': homeSaga,
    '/logout': function* () {
        // ...
    },
};

const combinedRouteHandlers = combineDependenciesHandlers(
    appHandlers,
    usersHandlers,
    reviewsHandlers
);

runRouteDependencies(combinedRouteHandlers);

Each module (e.g. Modules/users) may exports its own routeHandlers object and the combineDependenciesHandlers utility handles their merging.


HOC

fetchDependencies(config?: Config) => (Component) => ComponentThatFetchDependencies

High order component used to request data for wrapped component. If you wrap your page components with the HOC it will ensure that data it needs will be requested right after component render.

In default HOC calls fetch function passed through props when component mounts and clear before it unmounts. If it doesn't suit your needs, supply a config that define your own way of handling. All methods in config are optional and if they aren't supplied, default ones are used.

interface Config {
    onLoad?: (props) => void;
    onUnload?: (props) => void;
    shouldReFetch?: (prevProps, nextProps) => boolean;
}
Example - Use with default config
const UsersListPageContainer = compose(
    connect(
        state => ({ users: state.users }),
        dispatch => bindActionCreators({ 
            fetch: requestUsers,
            clear: deleteUsers 
        }, dispatch),
    ),
    fetchDependencies(),
)(UsersListPage);

const App = () => (
    <div>
        <UserListPageContainer />
    </div>
);
Example - Use with custom config
const UserDetailPageContainer = compose(
    connect(
        (state: State) => ({
            user: state.user,
            userId: state.selectedUserId
        }),
        dispatch => bindActionCreators({
            requestUserDetail: requestUser,
            clearUserDetail: clearUser,
        }, dispatch),
    ),
    fetchDependencies({
        onLoad: ({ userId, requestUserDetail }) => {
            requestUserDetail(userId);
        },
        onUnload: ({ clearUserDetail }) => {
            clearUserDetail();
        },
        shouldReFetch: (prevProps, props) => {
            return prevProps.userId !=== props.userId;
        },
    }),
)(UserDetailPage);

const App = () => (
    <div>
        <UserListPageContainer />
    </div>
);

routeDependencies(config?: Config) => (Component) => ComponentWithRouteDependencies

Alert Important prerequistence for using the HOC is that you must have react-router in your app.

HOC has same purpose and works almost same as fetchDependencies with few exceptions.

  • It wraps component also with withRouter HOC so all props receive also router's match, location and history objects.
  • Its config is slightly different especially in method names. Look at the definition: typescript interface Config { onRouteEnter?: (props) => void; onRouteLeave?: (props) => void; shouldReRoute?: (prevProps, nextProps) => boolean; }
  • Default implementation of shouldReRoute invoke refetch of data every time any part of url change. It's used until you provide your own.
Example - Use with default config
const UsersListPageContainer = compose(
    connect(
        state => ({ users: state.users }),
        dispatch => bindActionCreators({ 
            fetch: requestUsers,
            clear: deleteUsers 
        }, dispatch),
    ),
    routeDependencies(),
)(UsersListPage);

const App = () => (
    <Router>
        <div>
            <Route path="/users" component={UserListPageContainer}/>
        </div>
    </Router>
);
Example - Use with custom config
const UserDetailPageContainer = compose(
    connect(
        (state: State) => ({ user: state.user }),
        dispatch => bindActionCreators({
            requestUserDetail: requestUser,
            clearUserDetail: clearUser,
        }, dispatch),
    ),
    routeDependencies({
        onRouteEnter: ({ match, requestUserDetail }) => {
            requestUserDetail(match.params.id);
        },
        onRouteLeave: ({ match, clearUserDetail }) => {
            clearUserDetail();
        },
        shouldReRoute: (prevProps, props) => {
            return prevProps.match.params.id !=== props.match.params.id;
        },
    }),
)(UserDetailPage);

const App = () => (
    <Router>
        <div>
            <Route path="/users/:id" component={UserDetailPageContainer}/>
        </div>
    </Router>
);
更多

友情链接

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文