来源 | https://www.cnblogs.com/xhyccc/p/14242492.html
什么是 Service
-
Service 包含 n 个方法; -
Service 包含有状态; -
Service 应该是个单例。 -
这些方法与状态应该是高度整合的,一个 Service 解决的是一个模块的问题。
class TodoRecordService {
private todoList: Record[] = [];
get getTodoList() {
return this.todoList;
}
public addRecord(newRecord: Record) {
this.todoList.push(newRecord);
}
public deleteRecord(id: string) {
this.todoList = this.todoList.filter((record) => record.id !== id);
}
public getRecord(id: string) {
const targetIndex = this.todoList.findIndex((record) => record.id === id);
return { index: targetIndex, ele: this.todoList[targetIndex] };
}
}
自定义 Service
那我们用 React 如何实现一个状态共享的单例呢?
使用 Context 与 useContext 即可。
接下来我们做一个最简单的计数器吧:一个负责计数的 button,一个负责显示当前数值的 panel。
const App: React.FC = () => {
return (
<div>
<Button />
<Panel />
</div>
);
};
然后我们来定义我们的 Service:
interface State {
count: number;
handleAdd: () => void;
}
export const CountService = createContext<State>(null);
我们选择让一个 Context 成为一个 Service,这是因为我们可以利用 Context 的特性来进行状态共享,达到单例的效果。
但是光这样还不行,我们想让 count 拥有响应性,就必须使用 useState(或者其他 hook)来创建。
因此需要一个自定义 Hook,并且在 Context.Provider 中传入 Provider 的 value 值:
interface State {
count: number;
handleAdd: () => void;
}
export const CountService = createContext<State>(null);
export const useRootCountService = () => {
const [count, setCount] = useState<number>(0);
const handleAdd = useCallback(() => {
setCount((n) => n + 1);
}, []);
return {
count,
handleAdd,
};
};
那么在组建中,我们如何使用 Service 呢?
非常简单:
const App: React.FC = () => {
const countService = useContext(CountService);
return <div>{countService.count}</div>;
};
所以计数器的完整代码应该这么写:
import { CountService, useRootCountService } from './service/count.service';
const App: React.FC = () => {
return (
<CountService.Provider value={useRooCountService()}>
<div>
<Button />
<Panel />
</div>
</CountService.Provider>
);
};
// Button.tsx
import { CountService } from '../services/global.service';
const Button: React.FC = () => {
// 注意,此处是故意写复杂了,是为了凸显跨组件状态管理的特性
const countService = useContext(CountService);
return <button onClick={() => countService.handleAdd()}>+</button>;
};
// Panel.tsx
import { CountService } from '../services/global.service';
const Panel: React.FC = () => {
const countService = useContext(CountService);
return <h2>{countService.count}</h2>;
};
hooks 与 Service
对于小组件而言,刚刚的写法已经足够了。
但是要知道,Service 是高度集中的某个模块的状态与方法,我们不能保证 Service 的方法可以直接用到组件的逻辑中去。
所以需要我们在组件内部对于逻辑进行二次拼装。
但是把逻辑直接写到组件里面是一件非常恶劣的事情!!!
幸好,React 有了 hooks 让我们去抽离逻辑代码。
const useLogic1 = () => {
// 在 hook 中获取服务
const xxxService = useContext(XxxService);
// ...
const foo = useCallback(() => {
// ...
xxxService.xxxx();
// ...
}, []);
return {
// ...
foo,
};
};
const SomeComponent: React.FC = () => {
// 复用逻辑
const { a, b, foo } = useLogic1(someParams);
const { c, bar } = useLogic2();
return (
<div>
<button onClick={() => bar()}>Some Operation</button>
</div>
);
};
这种形式的组件,便是我们的目标。
最后
欢迎加我微信(winty230),拉你进技术群,长期交流学习...
欢迎关注「前端Q」,认真学前端,做个专业的技术人...
本文分享自微信公众号 - 前端Q(luckyWinty)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。
来源:oschina
链接:https://my.oschina.net/LuckyWinty/blog/4919474