好代码一定简洁吗?我在状态管理上的「不简洁」之道

116 阅读3分钟

首先,看一个这样的例子:

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 集成、异步操作、状态组合、特性分离等更多功能,

如果能再点个星星,那便是感激涕零了,

感谢阅读。