> For the complete documentation index, see [llms.txt](https://xztech.gitbook.io/servicex/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://xztech.gitbook.io/servicex/master.md).

# 快速上手

ServiceX是一个**Typescript Project Only**的基于React hooks api之上的轻量级状态管理工具，主要的特点和解决的痛点有：

1. **灵活多store**：可以自由粒度的定义store，避免了redux在大项目中单一store越来越庞大的问题，并且提供一套强大的依赖注入功能来管理store
2. **严格ts类型**：项目本身有严格ts编写，同时对外接口处处都有TS类型推断，再也不用担心redux中action type拼错或者payload类型不兼容等隐形bug，同时更容易进行代码定位。
3. **强大的rxjs集成**：所有副作用都是通过rxjs进行描述和管理，与redux-saga相比，rxjs更通用，更强大，ts类型提示和约束做的更好。

### 安装

```
npm i service-x immer inversify reflect-metadata rxjs
```

### 使用

#### 开启ts中的编译flag:

编辑tsconfig.json中的complerOptions:

```javascript
{
  "compilerOptions": {
    "strict": true, // 严格模式，建议开启
    "emitDecoratorMetadata": true, // 开启metadata支持
    "experimentalDecorators": true,  // 开启decorator支持
    //...
  }
}

```

{% hint style="info" %}
experimentalDecorators虽然是实验性质的，但是这个flag已经稳定好几个版本了，大名鼎鼎的Angular也使用到了这个特性，所以不必担心之后的兼容问题。
{% endhint %}

#### 第一个Service

service代表了一个store，其中定义了store的state以及与redux概念相似的reducer， effect等action:

```typescript
// 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结合

```typescript
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啦，这里有一个更加高级的在线示例：

<https://codesandbox.io/s/service-x-demo-9dqw6>
