首先,看一个这样的例子:
foo(300);
朋友你能否看出传入 foo 的参数是干什么的呢?
但要是这样呢:
const timeout = 300;
foo(timeout);
是不是就可以清晰地知道这个参数是控制超时用的了?
这便是著名的“魔法字面量”问题,尽管前者极尽简洁,但是后者用一个常量就大幅提高了可读性。
再看一个状态管理的例子:
let count = 0;
let delta = 1;
function increment() {
count += 1;
}
increment();
如果不读完 increment 的函数体:
let count = 0;
let delta = 1;
function increment() {
...
}
increment();
你能否确定 delta 被读或被写了呢?
但要是这样呢,并且约定这里的函数都是纯函数:
let count = 0;
let delta = 1;
function increment(count) {
return count + 1;
}
count = increment(count);
即使不读 increment 的函数体:
let count = 0;
let delta = 1;
function increment(count) {
...
}
count = increment(count);
是不是依然可以清晰地知道这个函数只会读写 count 而不会碰到 delta?
这样一来,
在不需要了解细节时我们便可以安全跳读函数体,
在需要时还能带着对变化范围的认识深入函数体,
从而大幅提高读码效率。
可见简洁的代码未必都好,不简洁的代码未必都不好。
所以,在简洁和不简洁之间到底应该怎么选择呢?
简洁与否,本质上其实是,给定相同的功能少写代码还是多写代码。
如果看不清楚多写代码带来的好处,自然是写得越少越好。
不过像前面的案例中那样,
以额外代码可以实现相对大的改善,此时不简洁就成了更好的选择。
而今天的状态库中,有的不计成本地追求某种改善,有的毫无顾忌地追求某种简洁,都不十分可取。
所以,我运用不简洁新写了一款状态库 React Mug:
npm i react-mug
再次回到前面的状态管理案例,以这个库实现的话:
import { construction, upon } from 'react-mug';
const countMug = { [construction]: 0 };
let delta = 1;
const { w } = upon(countMug);
const increment = w((count) => {
return count + 1;
});
increment();
由于 w 接收的是纯函数,在不读 increment 函数体的情况下:
const countMug = { [construction]: 0 };
let delta = 1;
const { w } = upon(countMug);
const increment = w((count) => {
...
});
increment();
我们依然能够清晰地知道这个函数只会读写 count 而不会碰到 delta。
从而大幅提高读码效率:
在不需要了解细节时安全跳读函数体,
在需要时带着对变更范围的认识深入。
而且值得一提的是,increment 在调用方式上保留了指令式的简洁,进一步降低了改善的代价:
const increment = w((count) => {
...
});
increment();
// ^^^^ 不用传入 count 就能调用
不过与此同时,increment 在测试方式上提供了函数式的直接,尽可能拉大了改善的幅度:
describe('increment', () => {
test('adds up count and 1', () => {
expect(increment(2)).toBe(3);
// ^^^ 直接传入 count 就能测试
});
});
可谓是为了好代码无微不至。
希望大家喜欢。
完整指南可以移步 GitHub Repo openquoll/react-mug 进行参考,
其中有 React 集成、异步操作、状态组合、特性分离等更多功能,
如果能再点个星星,那便是感激涕零了,
感谢阅读。