@ackee/chris 中文文档教程
Chris
对你的 redux - react-router 应用程序有用的补充。
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)
通过使用 runRouteDependencies
和 routing-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 thetype
of course) fromSET_PAGE_START
. TheSET_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 bygetData
handler. - When
getData
saga- throw an error during its run, action
REQUEST_USERS_FAILED
witherror
property is dispatched - run without error then action
REQUEST_USERS_SUCCEEDED
with propertyresult
is dispatched, where result is anything thatgetData
saga returns (nothing in our example ????)
- throw an error during its run, action
- Action
REQUEST_USERS_COMPLETED
is dispatched at the end every time, no matter if runninggetData
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
使用 HOC 的重要先决条件是您必须拥有
react-router
你的应用程序。
HOC 与 fetchDependencies
具有相同的目的和工作方式,几乎没有例外。
- It wraps component also with
withRouter
HOC so all props receive also router'smatch
,location
andhistory
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>
);