快速上手
https://github.com/stkevintan/ServiceX
ServiceX是一个Typescript Project Only的基于React hooks api之上的轻量级状态管理工具,主要的特点和解决的痛点有:
灵活多store:可以自由粒度的定义store,避免了redux在大项目中单一store越来越庞大的问题,并且提供一套强大的依赖注入功能来管理store
严格ts类型:项目本身有严格ts编写,同时对外接口处处都有TS类型推断,再也不用担心redux中action type拼错或者payload类型不兼容等隐形bug,同时更容易进行代码定位。
强大的rxjs集成:所有副作用都是通过rxjs进行描述和管理,与redux-saga相比,rxjs更通用,更强大,ts类型提示和约束做的更好。
安装
npm i service-x immer inversify reflect-metadata rxjs
使用
开启ts中的编译flag:
编辑tsconfig.json中的complerOptions:
{
"compilerOptions": {
"strict": true, // 严格模式,建议开启
"emitDecoratorMetadata": true, // 开启metadata支持
"experimentalDecorators": true, // 开启decorator支持
//...
}
}
第一个Service
service代表了一个store,其中定义了store的state以及与redux概念相似的reducer, effect等action:
// entry-service.ts
import {
Injectable,
Service,
Reducer,
ImmerReducer,
Effect,
EffectAction,
DefineAction
} from "service-x";
import { Entry, fetchEntries } from "./fetch-entries";
import { Observable, merge } from "rxjs";
import {
map,
distinctUntilChanged,
combineLatest,
switchMap,
debounceTime
} from "rxjs/operators";
interface State {
entries: Entry[];
keyword?: string;
}
@Injectable()
export class EntryService extends Service<State> {
defaultState: State = {
entries: []
};
// trigger loadEntries reload
@DefineAction()
reload$!: Observable<void>;
@ImmerReducer()
setKeyword(state: State, keyword: string) {
state.keyword = keyword;
}
@ImmerReducer()
setEntries(state: State, entries: Entry[]) {
state.entries = entries;
}
@Reducer()
reset(): State {
return this.defaultState;
}
@Effect()
loadEntries(
trigger$: Observable<void>,
state$: Observable<State>
): Observable<EffectAction> {
const keyword$ = state$.pipe(
map(state => state.keyword),
distinctUntilChanged(),
// debounce the input
debounceTime(500)
);
return merge(trigger$, this.reload$).pipe(
combineLatest(keyword$, (_, keyword) => keyword),
switchMap(keyword => fetchEntries(keyword)),
map(entries => this.actions().setEntries(entries))
);
}
}
与React结合
import React from "react";
import ReactDOM from "react-dom";
import { useService } from "service-x";
import { EntryService } from "./entry-service";
import "./index.less";
const App = () => {
const [state, actions] = useService(EntryService, { resetOnUnmount: true });
React.useEffect(() => {
// load the entries on component mounted
actions.loadEntries();
}, [actions]);
return (
<>
<input
className="search"
value={state.keyword}
onChange={e => actions.setKeyword(e.target.value)}
placeholder="Enter keywords to search"
/>
{state.entries.length ? (
<ul className="entries">
{state.entries.map(entry => (
<li className="entry" key={entry.name}>
<span>{entry.name}</span>
<small>{entry.age}</small>
</li>
))}
</ul>
) : (
<p className="empty">Nothing found</p>
)}
<button onClick={() => actions.reload$()}>刷新</button>
</>
);
};
ReactDOM.render(<App />, document.getElementById("app"));
一个简单的人名搜索应用就OK啦,这里有一个更加高级的在线示例:
Last updated
Was this helpful?