[Flutter小试牛刀] 低配版signals,添加多层监听链

本文续接上篇文章: #[Flutter小试牛刀] 写一个低配版的signals

在上篇文章里,我们已经写了一个减配版的signals,但它仍然有很多缺陷:

  1. 节点的改变监听应该自己维护,当获取自己的值时可以重置监听链。
  2. 它不支持多层监听链,computed里面只能引用use,用起来就很不方便。

我们先分析一下运用signals的代码:

dart 复制代码
bool flag = true;
final a = signal(0); 
final b = signal(1); 
final c = computed(() => a.value + b.value); 
final d = computed((){
    if(flag) return c.value +b.value;
    return b.value;
); 
final dispose = effect((){ print("d is ${d.value}"); }); 

expect(c.value, 1); 
expect(d.value, 2); 
a.value = 2; 
expect(c.value, 3); 
expect(d.value, 4); 
flag = false;
expect(c.value, 3); 
expect(d.value, 1); 
dispose();

从上面代码中可以看出 d监听了c值的改变,而c值也是算出来的,这就是需要我们的框架支持多层监听链。 我们还可以看到 flag的值决定了d变量的监听对象,如果flag = true,那么d需要监听c和b的值改变,如果flag = false,则只需要监听b的值改变。这就需要我们在每次调用d.value都需要重新建立它的监听体系。

为了解决问题1和问题2,我们对代码进行以下改造:

dart 复制代码
abstract mixin class Node {

    final Set<Node> children = {};
    
    
    //将自己的值改变绑定到节点
    void _subscribeToNode(Node node) {
        if (children.contains(node)) {
            return;
        }
        addListener(node.notifyListeners);
        node.children.add(this);
    }
    //取消将自己的值改变绑定到节点
    void unSubscribeToNode(Node node) {
        if (children.contains(node)) {
            node.removeListener(notifyListeners);
            children.remove(node);
        }
    }
    //清除所有监听
    void _unsubscribe() {
        for (var child in children) {
        child.removeListener(notifyListeners);

        }
        children.clear();
    }

    void addListener(void Function() listener);
    void removeListener(void Function() listener);
    void dispose();
    void notifyListeners();
   
}

class UseCompute<T> with Node {
    DisposeFn<T>? onDispose;
    ChangeFn<T>? onChange;
    final CreateFn<T> onCreate;
    final Set<void Function()> _listeners = {};
    T? _value;
    UseCompute(this.onCreate);
    
    T? get value {
        //如果全局节点不为空将自己的改变绑定到全局节点。
        if (_currentNode != null) {
            _subscribeToNode(_currentNode!);
        }
        //清除之前的监听节点列表。
        _unsubscribe();
        //缓存全局节点
        var oldCurrentNode = _currentNode;
        try {
        //将自己设置成全局节点
            _currentNode = this;
            //重新建立监听节点列表。
            var newValue = onCreate.call();
            //如果值有改变则回调
            if (_value != newValue) {
                onChange?.call(newValue);
            }
            _value = newValue;
            return _value;
        } catch (error) {
            rethrow;
        } finally {
            //将全局节点重置成之前的缓存值
            _currentNode = oldCurrentNode;
        }
    }
    //不添加监听获取值
    T? peek() {
        return _value;
    }
    
    @override
    void addListener(void Function() listener) {
        _listeners.add(listener);
    }

    @override
    void removeListener(void Function() listener) {
        _listeners.remove(listener);
    }

    @override
    void dispose() {
        _listeners.clear();
        _unsubscribe();
        onDispose?.call(_value);
        _value = null;
    }

    @override
    void notifyListeners() {
        for (var listener in _listeners) {
        listener.call();
    }
}

class UseCreate<T> extends UseCompute<T> {

    UseCreate(super.onCreate);
    
    @override
    T? get value {
        //如果全局节点不为空则将自己的改变绑定到全局节点。
        if (_currentNode != null) {
            _subscribeToNode(_currentNode!);
        }
        //如果值为空则尝试初始化它
        _value ??= onCreate.call();
        return _value;
    }

    set value(T? newValue) {
        if (_value == newValue) {
            return;
        }
            _value = newValue;
            notifyListeners();
            onChange?.call(_value);
        }
    }
}

整个改造,UseCompute的 get value方法是核心。他们核心步骤是:

1.如果全局节点不为空则自己的改变事件绑定到全局节点。 2.清除之前的监听节点列表。 3.缓存全局节点,将自己设置成全局节点。 4.重新建立监听节点列表。 5.将全局节点重置成之前的缓存值

自此所有改造已经完成

相关推荐
天天扭码12 分钟前
从数组到对象:JavaScript 遍历语法全解析(ES5 到 ES6 + 超详细指南)
前端·javascript·面试
拉不动的猪13 分钟前
前端开发中常见的数据结构优化问题
前端·javascript·面试
街尾杂货店&14 分钟前
css word
前端·css
Мартин.17 分钟前
[Meachines] [Hard] CrimeStoppers LFI+ZIP-Shell+Firefox-Dec+DLINK+rootme-0.5
前端·firefox
冰镇生鲜17 分钟前
快速静态界面 MDC规则约束 示范
前端
技术与健康31 分钟前
【解读】Chrome 浏览器实验性功能全景
前端·chrome
Bald Monkey38 分钟前
【Element Plus】解决移动设备使用 el-menu 和 el-sub-menu 时,子菜单需要点击两次才会隐藏的问题
前端·elementui·vue·element plus
小小小小宇1 小时前
PC和WebView白屏检测
前端
天天扭码1 小时前
ES6 Symbol 超详细教程:为什么它是避免对象属性冲突的终极方案?
前端·javascript·面试
小矮马1 小时前
React-组件和props
前端·javascript·react.js