alibaba / fish-redux

An assembled flutter application framework.
https://github.com/alibaba/fish-redux
Apache License 2.0
7.33k stars 843 forks source link

收集大家对fish-redux后续演进的期望 #96

Closed zjuwjf closed 5 years ago

zjuwjf commented 5 years ago

👏大家在这里发声,讨论。

  1. 拓展: 大家想要fish-redux做哪些能力拓展,水平的,垂直的?

  2. 工具: 大家对工具和插件的看法?

  3. 问题: fish-redux在大家使用中会遇到哪些问题 ? 哪些是目前大家使用flutter的普遍的痛点 ?

  4. 未来: 期望fish-redux成为一个什么样的开源产品

hyjfine commented 5 years ago

1,拓展

水平上的,增加全局Store的支持,类比vue的渐进开发思路,fish redux可以像传统redux那样使用,也可以搭配adapter作更极致的单页面优化。

2,插件

增加类似React dev tool这样的chrome 插件,可以对state检视,action时空穿梭,但现在的observatory也能凑合用。

3,问题

fish redux的注释和文档🌰可以更好

4,期望

在专业,友善,耐心,尊重氛围下推进,并能体现这些的开源产品。国际化,也期望它能像vue那样得到国内外工程师的认可和喜爱。

danieldai commented 5 years ago
  1. 插件 期待能够有针对 ide的开发插件,能够针对fish-redux框架层的元素进行提示,和错误检测,比如 https://github.com/alibaba/fish-redux/issues/97 这个case中遇到的问题,代码都是符合dart语法的,但是不符合fish-redux框架规则。另外,可以增加的一个提示是,在component中注册的sub component,在view中引用时,可以给出一定的提示。
hyjfine commented 5 years ago

补充一个flutter的痛点-hotfix

tom1918 commented 5 years ago

1,拓展

全局的监听器或者是 全局store的管理,(目前认为纯flutter 的项目开发需要这样的全局管理);

3,问题

同扩展,我要全局管理,我们目前正在尝试使用 flutter provide 结合fish-redux 来解决;

519818538 commented 5 years ago

3.问题 文档没有完全讲清楚,应该多使用一些图表说明原理。example中示例需要增加,作为一个标准框架,基本的示例必不可少,这样可以帮助新人快速上手。

4.未来 希望能成为Flutter业界标准框架,向vue学习,不断自我完善推进。

guopeng1994 commented 5 years ago

3.问题 文档!文档!!文档!!! 确实现在的文档不完善,一些用法和有些字段只有在源代码中能看到。其次就是demo,demo不一定要多复杂,展示基本用法即可。在一个就是上面说的全局状态管理了。

4.未来 因为是从RN转过来的,所以在使用了dva框架之后,觉得用起来很舒服,因此,我觉得可以借鉴dva的一些东西,让Fish-Redux更完善和人性化

chenyang88 commented 5 years ago

请问一个页面里面包含了 TabController , 这个应该怎么写? 我尝试写成这样 但是报错 没办法实现 TickerProviderStateMixin


class PageState with Cloneable<PageState>, TickerProviderStateMixin {

  TabController tabController;

  @override
  PageState clone() {
    return PageState()..tabController = tabController;
  }
}

PageState initState(Map<String, dynamic> args) {
  PageState state = PageState();
  TabController tabController = TabController(vsync: state, length: 2);
  state.tabController = tabController;
  return state;
}```

请大牛给出解法,谢谢~!
zjuwjf commented 5 years ago

请问一个页面里面包含了 TabController , 这个应该怎么写? 我尝试写成这样 但是报错 没办法实现 TickerProviderStateMixin

class PageState with Cloneable<PageState>, TickerProviderStateMixin {

  TabController tabController;

  @override
  PageState clone() {
    return PageState()..tabController = tabController;
  }
}

PageState initState(Map<String, dynamic> args) {
  PageState state = PageState();
  TabController tabController = TabController(vsync: state, length: 2);
  state.tabController = tabController;
  return state;
}```

请大牛给出解法,谢谢~!

58 欢迎以新开issue的形式来提问题。

watermelon-brother commented 5 years ago

文档文档,我需要文档和更多的demo啊

johnhollon commented 5 years ago

State应更好的支持final属性! 而不是像现在这样,得在initState或connector里面像变魔术那样搞些垃圾“技巧代码”。 别跟我说final属性用不到!

zjuwjf commented 5 years ago

State应更好的支持final属性! 而不是像现在这样,得在initState或connector里面像变魔术那样搞些垃圾“技巧代码”。 别跟我说final属性用不到!

就个别问题,建议是搜索老的issue,或者新开一个。很大可能,你的问题是有讨论过的。 无论如何,在github上,我们是在讨论问题,而不是来发泄情绪。

watermelon-brother commented 5 years ago

框架注释太少了啊,能给个你们框架设计图吗,lib库中的类之间的关系,现在分析源码都不好分析啊!

johnhollon commented 5 years ago

就个别问题,建议是搜索老的issue,或者新开一个。很大可能,你的问题是有讨论过的。 无论如何,在github上,我们是在讨论问题,而不是来发泄情绪。

也许你认为更好的支持final属性,是无关紧要的个别问题。 但无论从哪个意义上讲,这的确是我认为的能够提升你们这个框架的一个很重要的能力。

johnhollon commented 5 years ago

除了final属性外。框架能否提供静态工具方法,使statelesswidget更好地被应用。比如viewservice, dispach,context能够在statelesswidget中更简便的获得,而非必须使用statelesswidget的构造方法参数。

zjuwjf commented 5 years ago

就个别问题,建议是搜索老的issue,或者新开一个。很大可能,你的问题是有讨论过的。 无论如何,在github上,我们是在讨论问题,而不是来发泄情绪。

也许你认为更好的支持final属性,是无关紧要的个别问题。 但无论从哪个意义上讲,这的确是我认为的能够提升你们这个框架的一个很重要的能力。

如果你说的是immutable-state,那么它是已经在fish-redux中得到支持的。如果你说的是其他问题,那么需要将问题客观的描述清楚。

zhangruiyu commented 5 years ago

0-0大佬,你好! 有个不是fish_redux的问题,我想了解下咸鱼动态化方面的,但是找不到什么路径,我详细看了2篇关于这个的文章,目前遇到问题:1,final String name = ASTUtils.getNodeString(namedExp.name); if (name == 'children') { continue; }时为children为什么要跳过去呢,第一篇文章说widget 的children 放在list里面,但是并没有看到这样的步骤啊.如果可以想看下ASTUtils代码具体片段. 2,FunctionExpressionParser.parse(namedExp.expression)能否详细让看下呢,不太清楚这个步骤是把方法解析成什么返回回来,解析出的方法返回值,在客户端怎么用呢 3,交互事件怎么做动态化?这部分感觉很难懂的! 感觉自己好菜0-0,只能求助了!实在找不到方向0-0,能帮助下吗,感谢!

2534290808 commented 5 years ago

3.问题 文档!文档!!文档!!! 确实现在的文档不完善,一些用法和有些字段只有在源代码中能看到。其次就是demo,demo不一定要多复杂,展示基本用法即可。在一个就是上面说的全局状态管理了。

4.未来 因为是从RN转过来的,所以在使用了dva框架之后,觉得用起来很舒服,因此,我觉得可以借鉴dva的一些东西,让Fish-Redux更完善和人性化

兄弟,我也是,感觉dva用的特别爽

baij930312 commented 5 years ago

是否考虑支持 redux-persist 类似的功能?

danieldai commented 5 years ago

@baij930312 我在APP中做了个集成 fish_redux 和 redux-persist的 middleware,这种相关代码片段

    final persistor = Persistor<PageState>(
      storage: FlutterStorage(),
      serializer: JsonSerializer<PageState>(PageState.fromJson),
    );

    Middleware<PageState> persistMiddleware = (
        {Dispatch dispatch, Get<PageState> getState}) {
      return (Dispatch next) {
        return (Action action) {
          next(action);
          if (action is PersistAction) {
            print('PersistState: ${action.type} ${action.payload}');
            persistor.save(getState());
          }
        };
      };
    };

    var initialState;
    try {
      initialState = await persistor.load();
    } on SerializationException {
      // 第一次加载程序时,还没有任何数据,加载数据会失败
      // 此时使用初始化数据
      initialState = PageState();
    }
baij930312 commented 5 years ago

@baij930312 我在APP中做了个集成 fish_redux 和 redux-persist的 middleware,这种相关代码片段

    final persistor = Persistor<PageState>(
      storage: FlutterStorage(),
      serializer: JsonSerializer<PageState>(PageState.fromJson),
    );

    Middleware<PageState> persistMiddleware = (
        {Dispatch dispatch, Get<PageState> getState}) {
      return (Dispatch next) {
        return (Action action) {
          next(action);
          if (action is PersistAction) {
            print('PersistState: ${action.type} ${action.payload}');
            persistor.save(getState());
          }
        };
      };
    };

    var initialState;
    try {
      initialState = await persistor.load();
    } on SerializationException {
      // 第一次加载程序时,还没有任何数据,加载数据会失败
      // 此时使用初始化数据
      initialState = PageState();
    }

cool~ 你是基于整个app只有一个store的策略下的嘛,另外如果要支持这种功能的话所有的state都需要支持fromJson和toJson了吧

danieldai commented 5 years ago

是的,我只有一个store需要存储,这个store下所有的state 都做了fromJson和toJson,另外我并非所有的action都需要触发存储,就做了个叫 PersistAction Action子类,只有PersistAction才触发save动作。

dingchaoyan1983 commented 5 years ago

如果我需要在state之上在加一个计算层,该如何实现,比如api返回的数据比较零碎,我需要将需要将多个model的数据进行组装之后才可以在ui层演示这个没有没考虑加一个回调让用户自己来处理

zjuwjf commented 5 years ago

如果我需要在state之上在加一个计算层,该如何实现,比如api返回的数据比较零碎,我需要将需要将多个model的数据进行组装之后才可以在ui层演示这个没有没考虑加一个回调让用户自己来处理

这里就是effect中做的事情。

dingchaoyan1983 commented 5 years ago
如果我需要在state之上在加一个计算层,该如何实现,比如api返回的数据比较零碎,我需要将需要将多个model的数据进行组装之后才可以在ui层演示这个没有没考虑加一个回调让用户自己来处理

依照我最近对fish-redux源码的研究,作者好像是想要开发者在connector里面去做我上面提到的问题

longgui0318 commented 5 years ago

3 问题: 3.1 文档比较零散,很多关键问题,比如调用的一些关键时序图没有,实际投入使用看得比较累

4 未来: 4.1 需要一个比较有更多内容的例子来引导新人使用 4.2 更多的参考dva的设计,fish-redux这一层的所做的其实参考dva能够做得更好,如 loading状态的设计,可以简化很多手写的 success、failure Action的处理 4.3 全局store目前的设计比较破坏原有的结构,导致page initialState功能闲置,需要更加内聚的方式代替action的方式

ykrank commented 5 years ago

ViewService的buildComponent希望添加extra参数,同样的,在对应的Dependent的buildComponent,和connector中也添加extra参数。 现在存在一种需求,不定数量的component复用,在state中对应存储的数据格式为list或者map<key, value>。这时候需要在buildComponent和connector中指定具体的key来获取正确的数据。

zjuwjf commented 5 years ago

ViewService的buildComponent希望添加extra参数,同样的,在对应的Dependent的buildComponent,和connector中也添加extra参数。 现在存在一种需求,不定数量的component复用,在state中对应存储的数据格式为list或者map<key, value>。这时候需要在buildComponent和connector中指定具体的key来获取正确的数据。

添加了extra参数,本质上数据就不单一了。你具体讲下你的场景么?

ykrank commented 5 years ago

ViewService的buildComponent希望添加extra参数,同样的,在对应的Dependent的buildComponent,和connector中也添加extra参数。 现在存在一种需求,不定数量的component复用,在state中对应存储的数据格式为list或者map<key, value>。这时候需要在buildComponent和connector中指定具体的key来获取正确的数据。

添加了extra参数,本质上数据就不单一了。你具体讲下你的场景么?

我上面已经解释了呀,同一page有不定数量的component复用,它们的数据完全是来自于store里面的一个list,list的每一条对应一个component,目前考虑extra里面是放能标识component实例和对应数据的key,只是考虑扩展,使用extra。 从数据层面讲,你可以认为是更高形式的Adapter。不能使用Adapter的原因是使用了AppRoute之后,每一个page都是component,完全有可能是list的每一条对应一个page。 这个和单一数据也并不冲突。

MMMzq commented 5 years ago

ViewService的buildComponent希望添加extra参数,同样的,在对应的Dependent的buildComponent,和connector中也添加extra参数。 现在存在一种需求,不定数量的component复用,在state中对应存储的数据格式为list或者map<key, value>。这时候需要在buildComponent和connector中指定具体的key来获取正确的数据。

添加了extra参数,本质上数据就不单一了。你具体讲下你的场景么?

我上面已经解释了呀,同一page有不定数量的component复用,它们的数据完全是来自于store里面的一个list,list的每一条对应一个component,目前考虑extra里面是放能标识component实例和对应数据的key,只是考虑扩展,使用extra。 从数据层面讲,你可以认为是更高形式的Adapter。不能使用Adapter的原因是使用了AppRoute之后,每一个page都是component,完全有可能是list的每一条对应一个page。 这个和单一数据也并不冲突。

+1

qq329401134 commented 5 years ago

可以获取异步effect的状态吗,比如我做登陆功能,然后点击提交后获取对应的effect应该是loading状态,当服务器返回数据effect代码运行完以后,effect的状态获取为done

zjuwjf commented 5 years ago

可以获取异步effect的状态吗,比如我做登陆功能,然后点击提交后获取对应的effect应该是loading状态,当服务器返回数据effect代码运行完以后,effect的状态获取为done

ctx.state

可以获取最新的状态

codlin commented 5 years ago

诚心请教:能不能讲讲为什么选择了Redux而没有选择BLoC,是BLoC在技术上有什么缺陷吗?

zjuwjf commented 5 years ago

我上面已经解释了呀,同一page有不定数量的component复用,它们的数据完全是来自于store里面的一个list,list的每一条对应一个component,目前考虑extra里面是放能标识component实例和对应数据的key,只是考虑扩展,使用extra。 从数据层面讲,你可以认为是更高形式的Adapter。不能使用Adapter的原因是使用了AppRoute之后,每一个page都是component,完全有可能是list的每一条对应一个page。 这个和单一数据也并不冲突。

1、 推荐使用PageRoutes 2、数据驱动的扩展 #239

fish-redux 的数据驱动是store 扁平的通知到各个组件。

1. 组件如何确定自己要刷新

默认是根据组件的state是不是同一个对象的引用来决定。(除非我们自定义shouldUpdate) 这里会有几个关注点: a)如果我们直接修改state.field 将会判断为同一个对象,所以我们需要clone或其他手段,在reducer函数中返回新的state。(它的上级state,上上级state的clone,是框架自动会处理的)。 b)组件的state如何而来?它是通过我们配置的connector.get 而来。如果我们的get函数永远返回一个新的对象,它不是最佳实践,带来多余的组件刷新。这种情况下要考虑使用reselect来将上级state的若干要素,只有在对应要素发生变化的情况下才会生成新的下级state。

2. 在PageStore的场景下

  1. 组件如何确定自己要刷新 这里和上面的规则是一样的

  2. 如何让appstore的变化也能精确驱动组件的变化 这里的精确指的是,只有在对应要素发生变化的时候才发生刷新。 a)将AppState 和 PageState 进行链接, 当AppState里有PageState关心的要素发生变化时,自动计算出最新PageState。 b)PageState的精确变化,驱动关心了PageState里对应要素的任意组件发生刷新。 这个过程是精确而清晰的。

fish-redux中的数据驱动的核心是connector。

hzgotb commented 5 years ago

老哥你好。最近遇到一个问题,每一个componen的state都会有一个overirde的clone方法。我不知道当时设计考虑到什么,但开发体验上,每个clone都需要去级联赋值,如果state多的话,这是一个耗时的重复性操作。我觉得对于clone方法没有必要去override。

如果要内置处理clone的话,需要遍历原state class的field。这一块可能会用到reflectable这个包。

下午试了一下。成本有点高。需要引入两个包。新增一个State基类。然后页面的state类通过reflectable做映射。还有运行前先要build一个映射文件。

MMMzq commented 5 years ago

老哥你好。最近遇到一个问题,每一个componen的state都会有一个overirde的clone方法。我不知道当时设计考虑到什么,但开发体验上,每个clone都需要去级联赋值,如果state多的话,这是一个耗时的重复性操作。我觉得对于clone方法没有必要去override。

如果要内置处理clone的话,需要遍历原state class的field。这一块可能会用到reflectable这个包。

下午试了一下。成本有点高。需要引入两个包。新增一个State基类。然后页面的state类通过reflectable做映射。还有运行前先要build一个映射文件。

老哥,我现在的做法是,配合dart的analyzer和build库,进行自动化生成clone方法,基本上你在state修改或者添加属性都能实时监测到生成新的clone方法,而且不需要运行时,对于性能没有任何影响

hzgotb commented 5 years ago

老哥你好。最近遇到一个问题,每一个componen的state都会有一个overirde的clone方法。我不知道当时设计考虑到什么,但开发体验上,每个clone都需要去级联赋值,如果state多的话,这是一个耗时的重复性操作。我觉得对于clone方法没有必要去override。 如果要内置处理clone的话,需要遍历原state class的field。这一块可能会用到reflectable这个包。 下午试了一下。成本有点高。需要引入两个包。新增一个State基类。然后页面的state类通过reflectable做映射。还有运行前先要build一个映射文件。

老哥,我现在的做法是,配合dart的analyzer和build库,进行自动化生成clone方法,基本上你在state修改或者添加属性都能实时监测到生成新的clone方法,而且不需要运行时,对于性能没有任何影响

老哥能不能弄个demo看一下。

qq329401134 commented 5 years ago

可以获取异步effect的状态吗,比如我做登陆功能,然后点击提交后获取对应的effect应该是loading状态,当服务器返回数据effect代码运行完以后,effect的状态获取为done

ctx.state

可以获取最新的状态

我说的是获取异步effect的执行状态哦,从哪里看

hzgotb commented 5 years ago

可以获取异步effect的状态吗,比如我做登陆功能,然后点击提交后获取对应的effect应该是loading状态,当服务器返回数据effect代码运行完以后,effect的状态获取为done

ctx.state

可以获取最新的状态

我说的是获取异步effect的执行状态哦,从哪里看

我的登陆目前是这样写的

_login(Action action, Context<SignInState> ctx) async {
  try {
    // final uid = ctx.state.uidController.text;
    if (uid == '') {
      throw new Exception('请输入账号');
    }
    // final pwd = ctx.state.pwdController.text;
    if (pwd == '') {
      throw new Exception('请输入密码');
    }
    ctx.dispatch(SignInActionCreator.loading(true));
    final res = await api.user.auth(uid, pwd);
    if (res.error) {
      throw new Exception(res.message);
    }
    final user = res.data;
    globalStore.dispatch(GlobalActionCreator.cacheUserInfo(user));
    api = Api.auth(user.token);
    Navigator.of(ctx.context).pushReplacementNamed('home');
  } catch (e) {
    errorDialog(e.message, ctx.context);
  } finally {
    if (ctx.state.loading) {
      ctx.dispatch(SignInActionCreator.loading(false));
    }
  }
}
zjuwjf commented 5 years ago

可以获取异步effect的状态吗,比如我做登陆功能,然后点击提交后获取对应的effect应该是loading状态,当服务器返回数据effect代码运行完以后,effect的状态获取为done

ctx.state

可以获取最新的状态

我说的是获取异步effect的执行状态哦,从哪里看

我的登陆目前是这样写的

_login(Action action, Context<SignInState> ctx) async {
  try {
    // final uid = ctx.state.uidController.text;
    if (uid == '') {
      throw new Exception('请输入账号');
    }
    // final pwd = ctx.state.pwdController.text;
    if (pwd == '') {
      throw new Exception('请输入密码');
    }
    ctx.dispatch(SignInActionCreator.loading(true));
    final res = await api.user.auth(uid, pwd);
    if (res.error) {
      throw new Exception(res.message);
    }
    final user = res.data;
    globalStore.dispatch(GlobalActionCreator.cacheUserInfo(user));
    api = Api.auth(user.token);
    Navigator.of(ctx.context).pushReplacementNamed('home');
  } catch (e) {
    errorDialog(e.message, ctx.context);
  } finally {
    if (ctx.state.loading) {
      ctx.dispatch(SignInActionCreator.loading(false));
    }
  }
}

非常优雅的代码。

有个小问题,你的异常处理在哪里?

hzgotb commented 5 years ago

catch里。会把错误信息通过dialog显示出来,没做其他的操作。我是不是少了返回一个非空值让事件不再传递?

zjuwjf commented 5 years ago

嗯,并不需要额外的非空值的返回, combineEffects -api 会补上非空值。

我看错了,我原以为是在外部会有一个统一的面向业务异常的处理中心。

icecreamco commented 5 years ago

注释。。求注释。。对于没有用过redux的端同学来说,注释太少读起源吗来太难受了~

codlin commented 5 years ago
typedef TypedApplyLike<R> = R Function(List<dynamic>, [Map<Symbol, dynamic>]);

/// Unified abstraction of functions which used in [Function.apply]
typedef ApplyLike = dynamic Function(List<dynamic>, [Map<Symbol, dynamic>]);

/// Unified abstraction of function AOP, input one function output another with some enhancement inside.
typedef ApplyLikeEnhancer = ApplyLike Function(ApplyLike functor);

ApplyLike _identity(ApplyLike f) => f;

ApplyLikeEnhancer _combine(ApplyLikeEnhancer e0, ApplyLikeEnhancer e1) =>
    (ApplyLike f) => (e1 ?? _identity)((e0 ?? _identity)(f));

顶层的文档太少了,代码看起来有点吃力……

huang12zheng commented 5 years ago

1.能力拓展:
(it is no urgely) 1.1根据 page 自动生成route? source from umi 1.2 如果A是B的子集,那么有一种方法可以生成实现get、set的defaultconn。 A[a,b,c,d,e] B[b,c]

  1. dependencies is not simple once I am,at a loss,learning about pool and slots ....
zjuwjf commented 5 years ago

注释。。求注释。。对于没有用过redux的端同学来说,注释太少读起源吗来太难受了~

1.能力拓展: (it is no urgely) 1.1根据 page 自动生成route? source from umi 1.2 如果A是B的子集,那么有一种方法可以生成实现get、set的defaultconn。 A[a,b,c,d,e] B[b,c]

  1. dependencies is not simple once I am,at a loss,learning about pool and slots ....

感谢反馈。 1.1 目前路由层是比较简易的,但是暂时没有规划fish-redux会完整的自己去实现一次路由层,我们的设计目标是能按需结合其他的路由框架。

1.2 conn的部分 ,建议可以单独开一个issue,来讨论是否当前已经是足够了,还是有必要继续提升。

1.3 的部分,目前在整理一部分的内部实现和增加注释, 期望更加易懂易用。

zjuwjf commented 5 years ago
typedef TypedApplyLike<R> = R Function(List<dynamic>, [Map<Symbol, dynamic>]);

/// Unified abstraction of functions which used in [Function.apply]
typedef ApplyLike = dynamic Function(List<dynamic>, [Map<Symbol, dynamic>]);

/// Unified abstraction of function AOP, input one function output another with some enhancement inside.
typedef ApplyLikeEnhancer = ApplyLike Function(ApplyLike functor);

ApplyLike _identity(ApplyLike f) => f;

ApplyLikeEnhancer _combine(ApplyLikeEnhancer e0, ApplyLikeEnhancer e1) =>
    (ApplyLike f) => (e1 ?? _identity)((e0 ?? _identity)(f));

顶层的文档太少了,代码看起来有点吃力……

你反馈的是AOP的源码的部分,建议可以就这个问题,单独开一个issue, 在讨论中,我们可以完善注释和文档, 使其易读易懂易用。

zjuwjf commented 5 years ago

注释。。求注释。。对于没有用过redux的端同学来说,注释太少读起源吗来太难受了~

可以就具体的哪一块注释的缺失,提issue,在讨论中,我们可以完善注释和文档, 使其易读易懂易用。

marscj commented 5 years ago

希望可以添加 观察者模式

zjuwjf commented 5 years ago

希望可以添加 观察者模式

能具体说下么,建议新开一个issue把。

liangwei0101 commented 5 years ago

使用fish-redux,多语言咋办呢?如果用他原生的方式是好整的,但是用了fish-redux不会触发更新。如果我要每次在Effect中去拦截,强制性去更新一遍绑定的数据是可以的。但是每个页面都要设置一把,多写了很多重复的代码。