3

New to Mithril but really want to like it. I coded demo app a few weeks ago using React and Redux for state management. I also used a library to connect the two, but it all works and can be seen at http://curiousercreative.com/demos/bankDemo/. After reading up on Mithril and liking a lot of what I read, I decided to translate the same demo app into Mithril + Redux, but I can't seem to get my Mithril mounted components to update/redraw and so updates to my Redux store (state) aren't ever reflected in the UI. From my app's js/app.js lines ~144-162, I have my Redux callback and my two top level Mithril components being mounted and passed our state.

// redraws Mithril whenever an action is dispatched
    store.subscribe(function () {
      console.log('happening');
      m.redraw.strategy('all');
      m.redraw(true);
    });

// Render Mithril
    // content
    m.mount(
      document.getElementById('content'),
      m.component(App, store.getState())
    );

    // nav
    m.mount(
        document.getElementById('navContainer'),
        m.component(Nav, store.getState())
    );

If you open a console at http://curiousercreative.com/demos/bankDemo-mithril/, you'll notice a log entry every time you click on a new link (which changes the hash which updates the Redux state store). You'll also notice in the code above that every time that log occurs, a forced redraw of Mithril is supposed to occur, but of course the UI doesn't change. Compare it's behavior to the React demo app further above. In the app's js/components.js file, our two top level components take this state object as an argument and pass it down to their child components.

  var App = {
    controller: function (args) {
      this.activePageId = args.activePageId;
      this.accounts = args.accounts;
      ...

  var Nav = {
    controller: function (args) {
      this.activePageId = args.activePageId;
      this.accounts = args.accounts;
      ...

Why isn't Mithril redrawing?

4

1 回答 1

3

问题是getState()在初始化时只调用一次,因此组件将始终使用对相同原始静态数据存储快照的引用进行重绘。

当顶层组件被挂载时,它们的控制器执行,它们的视图被编译和构建,可以访问控制器和任何传入的参数。控制器在初始化时只执行一次——当重绘触发时,视图函数被重新执行。这意味着嵌套组件可以传递其视图可以访问的新参数,但已安装的(顶级)组件具有其参数绑定,并且由于它们本身不是任何视图的后代,因此它们必须确定自己的逻辑更新参考。

查看您的代码时会跳出一些想法:

您可以在 mount 中传入getState方法(不调用它) - Mithril 非常支持在视图中使用函数进行数据输入和检索(如m.prop 所示,这是一种用于简单模型管理的内部工具):

var component = {
  view : function( ctrl, accessor ){
    return m( 'p', accessor().value )
  }
}

m.mount( document.body, m.component( component, store.getState ) )

动态选择器属性往往在属性映射中读取得更好。请注意,您可以将选择器字符串类与属性映射类结合起来。在控制器中声明的activePage函数看起来像是关注点分离,但由于视图需要与该特定方法挂钩,因此该函数显式用于生成类名,并且逻辑非常简单,您可以显着降低复杂性和通过将该逻辑直接放在视图中来提高可读性:

m('div.page', {
  id    : args.id,
  class : args.id === args.activePageId ? 'active' : ''
}, /* ... */ )

请注意,您不需要将虚拟 DOM 子项显式包装在数组中:您可以按顺序包含它们。实际上,这意味着方括号在 Mithril 虚拟 DOM 语法中是多余的:

m( '.parent',
  m( '.child1' ),
  m( '.child2' )
) 
于 2015-11-16T13:29:02.783 回答