当一个人写了多年的代码之后,就会开始变得懒惰。倒不是说懒得工作赚钱了,而是懒得写重复的代码了。笔者在一家公司常年使用react + redux + antd 三件套,不得不说写的好烦,状态管理一顿操作后发现还被困在模板代码里无法自拔。
写一个reducer到底有多费劲?
typescript
export const MY_ACTION_TYPE = 'my_type'
export const myAction = (payload) => {
return {
type: MY_ACTION_TYPE,
payload,
}
}
export const myReducer = (state = {}, action) => {
switch (action.type) {
case MY_ACTION_TYPE:
return { ...state, ...action.payload };
break;
default:
return state;
}
}
export const reducers = combineReducers({
myReducer,
})
难以忘怀的吐槽点:
- 重复地写ACTION_TYPE,它是连接action和reducer的暗号
- 重复的action模板
- 重复的reducer模板,千篇一律的
switch/case
以及烦人的扩展操作符 - 总是忘记在store实例导入reducer,导致数据不存在
- reducer的action.payload会根据action.type变化,难以融合
TS类型
这能忍?作为懒人的我肯定是忍不了的。有这时间写重复模板,我为啥不拿去划水吹逼增进感情?
找找其它状体库吧:
- dva :为啥那么多
yield
语法?怎么跟TS结合?好久不更新了?还绑定了路由?有事先走一步 - rematch:整体不错,但对TS的支持稍微弱了一些,至少还没让我很满意
- recoil:提供的api有点多,看得头皮发麻。而且不支持class组件
- jotai :就两个api,可以1分钟上手,类似
setState
。可惜就是太纯粹了,不支持异步操作,毕竟大部分场景我们都需要请求接口并存入数据。 - mobx:一个Proxy走天下,随时随地都可以改状态,项目大了不可控。另外装饰器现在还是残次品,个人不喜欢。(但是mobx作者写的immer真香)
- redux-toolkit:redux官方出品,虽然简洁不少,但还是没有跳出redux的禁锢
其实也有不少国内外开发者开源了自己的状态库。有些很优秀,但可能宣传不到位。有些维护一段时间就弃坑(公司KPI,失业转行,能力受限)了,留下一地鸡毛。有些纯粹是50行代码玩具,但是拥护者极多。
苦不堪言。既要好用,又要好学,还要完美支持Typescript,同时这个开源项目不能弃坑?身为一个拥有10年经验的程序员,当然是自己造轮子啦。
谈谈我的乌托邦幻想:
- 它是一个模块,一份状态就在一个文件里,包括相关的变更操作
- 不想再多写任何一行重复的代码
- 打破禁锢。我们的项目里其实一直只有一个store不是吗?
- 能提供异步函数,还要能知道函数的执行状态
- 必须完美支持Typescript!!
- 诱人的计算属性
- 不要复杂的概念,能让开发者不看文档都能猜到个大概
结合各家优点,一个新的状态库诞生:foca
(foca.js.org)
typescript
import { dfineModel } from 'foca';
import axios from 'axios';
const initialState: { name: string; age: number; posts: object[] } = {
name: 'foo',
age: 30,
posts: [],
}
export const profileModel = defineModel('profile', {
initialState,
reducers: {
udpateAge(state, age: number) {
state.age = age;
},
},
methods: {
async getPosts() {
const result = await axios.get<object[]>('/api/posts')
this.setState({ posts: result });
},
async getProfile() {
const result = await axios.get<{ age: number }>('/api/profile');
this.updateAge(result.age);
},
},
computed: {
firstPost() {
return this.state.posts[0];
},
postCount() {
return this.state.posts.length;
},
},
});
似乎我想要的都在里面了?模块化、没有模板、异步支持、完美的TS提示(没有写类型的地方都能自动提示)、计算属性、概念简单(看名字就知道是啥)。
不,还远远不够。前面提到了,大多数的项目里,其实只有需要一个store,所以foca也这么认为,它变成了单例模式
。有了这个前提,事情就好办多了,我们不需要dispatch ,不需要注册,导出即可使用,别提多方(懒)便(惰)了。
typescript
import { type FC,useEffect } from 'react';
import { useModel, useComputed, useLoading } from 'foca';
import { profileModel } from './profile.model';
const App: FC = () => {
const username = useModel(profileModel, (state) => state.name);
const postCount = useComputed(profileModel.postCount);
const loading = useLoading(profileModel.getProfile, profileModel.getPosts);
useEffect(() => {
profileModel.getProfile();
profileModel.getPosts();
}, []);
if (loading) return <p>Loading...</p>;
return <p>Hello { username }, you have { postCount } posts.</p>
}
export default App;
有没有耳目一新的感觉呢?视图逻辑内不再指定TS的类型,因为都是自动推导
出来了。业务逻辑也是清晰明了,恰到好处,整体写完就一个字: 爽!它基于redux,但开发者不再需要了解redux,用最简单粗暴直观的方式即可完成功能。
总结:
程序员因为懒而封装了各种轮子提升开发效率,轮子的愿景就是让使用者感觉到幸福,使用时温和不聒噪,使用后回味无穷,谈论时赞美有加。
foca
,流畅的状态管理库 foca.js.org