javascript - 误解 `each...in` 用 react 变量渲染

标签 javascript meteor meteor-blaze spacebars

我是 的新手 meteor /火焰 但这就是我公司正在使用的。

我很难理解 Blaze 如何决定渲染基于 ReactiveDict 的内容。

TL;博士

我从 ReactiveDict 创建了一些子模板父数组中的数组。当 ReactiveDict 时,子模板中的数据不会刷新。变化,我不明白为什么。

我可能对 Blaze 渲染有误解。你能帮帮我吗?

家长

父 Html 模板

<template name="parent">
    {{#each child in getChildren}}
        {{> childTemplate (childArgs child)}}
    {{/each}}
</template>

父 Javascript

react 变量

该模板呈现来自 getChildren 的子模板仅检索 ReactiveDict 的助手.

// Child data object
const child = () => ({
  id: uuid.v4(),
  value: ""
});

// Create a reactive dictionary
Template.parent.onCreated(function() {
  this.state = new ReactiveDict();
  this.state.setDefault({ children: [child()] });
});

// Return the children from the reactive dictionary
Template.parent.helpers({
  getChildren() {
    return Template.instance().state.get('children');
  }
});

子模板参数(来自 模板)

父模板为子模板提供了一些用于设置默认值和回调的数据。
每个都使用 childArgs 实例化使用 child 的 id 的函数设置正确的数据和回调。
单击 add 时按钮,它将一个 child 添加到 children ReactiveDict 中的数组.
单击 delete 时按钮,它会从 children 中删除 child ReactiveDict 中的数组.

Template.parent.helpers({
    // Set the children arguments: default values and callbacks
    childArgs(child) {
        const instance = Template.instance();
        const state = instance.state;
        const children = state.get('children');
        return {
           id: child.id,
           // Default values
           value: child.value, 
           // Just adding a child to the reactive variable using callback
           onAddClick() {
                const newChildren = [...children, child()];
                state.set('children', newChildren); 
           },
           // Just deleting the child in the reactive variable in the callback
           onDeleteClick(childId) { 
                const childIndex = children.findIndex(child => child.id === childId);
                children.splice(childIndex, 1);
                state.set('children', children); 
            }
        }
    }
})

child

子 html 模板

该模板显示来自父级和 2 个按钮的数据,adddelete .

<template name="child">
        <div>{{value}}</div>
        <button class="add_row" type="button">add</button>
        <button class="delete_row" type="button">delete</button>
</template>

子 javascript(事件)

这里调用的两个函数是作为参数从 传递的回调函数。家长 模板。

// The two functions are callbacks passed as parameters to the child template
Template.child.events({
  'click .add_row'(event, templateInstance) {
    templateInstance.data.onAddClick();
  },
  'click .delete_row'(event, templateInstance) {
    templateInstance.data.onDeleteClick(templateInstance.data.id);
  },

问题

我的问题是当我删除一个 child 时(使用回调来设置 ReactiveDict 就像 onAddClick() 函数),我的数据没有正确呈现。

文本示例:

我添加这样的行。
child 1 | value 1
child 2 | value 2
child 3 | value 3

当我删除 child 2 ,我明白了:
child 1 | value 1
child 3 | value 2

我想要这个:
child 1 | value 1
child 3 | value 3

我正在初始化 child来自 childArgs 的数据在 Template.child.onRendered()功能。
  • 不错 : getChildren()删除 child 时调用函数在 ReactiveDict并且我在变量中有正确的数据( children 中的 ReactiveDict )。
  • 不错 : 如果我有 3 个 child ,我删除了一个,家长 模板只呈现 2 个 child 。
  • : 然而 child 的onRendered()永远不会被调用( child 的 onCreated() 函数也不是)。这意味着为 显示的数据 child 模板错误。

  • 图片示例

    我正在添加图片以帮助理解:

    正确的html

    显示的 HTML 是正确的:我有 3 个 child ,我删除了第二个。在我的 HTML 中,我可以看到显示的两个 child 在其 div 中具有正确的 ID。然而显示的数据是错误的。

    correct html

    过时的数据

    我已经删除了第一张照片中的第二个 child 。显示的 child 应该是第一个和第三个。
    在控制台日志中,我的数据是正确的。红色数据是第一位的。紫色是第三个。

    然而我们可以看到被删除的 child 的数据显示出来了( asdasdasd )。删除标签时,我可以在日志中看到第二个 child 的 ID,尽管它不应该再存在了。第二个 child ID 为绿色。

    stale data

    我可能误会了什么。你能帮帮我吗?

    最佳答案

    我不知道从哪里开始,但有很多错误,我宁愿在这里提供一个运行中的解决方案并附上评论。

    首先是each函数应该正确地传递 id 而不是整个 child 或 find会导致错误:

    <template name="parent">
      {{#each child in getChildren}}
        {{#with (childArgs child.id)}}
          {{> childTemplate this}}
        {{/with}}
      {{/each}}
    </template>
    

    在帮助程序中,您可以避免调用过多的 Template.instance()使用 lexical scoping 函数:

    childArgs (childId) {
      const instance = Template.instance()
      const children = instance.state.get('children')
      const childData = children.find(child => child.id === childId)
      const value = {
        // Default values
        data: childData,
    
        // Just adding a child to the reactive variable using callback
        onAddClick () {
          const children = instance.state.get('children')
          const length = children ? children.length : 1
          const newChild = { id: `data ${length}` }
          const newChildren = [...children, newChild]
          instance.state.set('children', newChildren)
        },
    
        // Just deleting the child in the reactive variable in the callback
        onDeleteClick (childId) {
          const children = instance.state.get('children')
          const childIndex = children.findIndex(child => child.id === childId)
          children.splice(childIndex, 1)
          instance.state.set('children', children)
        }
      }
      return value
    }
    

    然后注意,在事件回调 'click .delete_row'您正在使用 templateInstance.data.id但这是 总是 undefined与您当前的结构。应该是 templateInstance.data.data.id因为data总是为模板实例中的所有数据定义,如果您命名属性 data那么您必须通过 data.data 访问它:

    Template.childTemplate.events({
      'click .add_row' (event, templateInstance) {
        templateInstance.data.onAddClick()
      },
      'click .delete_row' (event, templateInstance) {
        templateInstance.data.onDeleteClick(templateInstance.data.id)
      }
    })
    

    现在,为什么您的数据被奇怪地排序也是有道理的。看看onDeleteClick打回来:
    onDeleteClick (childId) {
      // childId is always undefined in your code
      const children = instance.state.get('children')
      const childIndex = children.findIndex(child => child.id === childId)
      // childIndex is always -1 in your code
      // next time make a dead switch in such situations:
      if (childIndex === -1) {
        throw new Error(`Expected child by id ${childId}`)
      }
      children.splice(childIndex, 1)
      // if index is -1 this removes the LAST element
      instance.state.set('children', children)
    }
    

    所以你的问题是拼接行为并将未经检查的索引传递给拼接:

    The index at which to start changing the array. If greater than the length of the array, start will be set to the length of the array. If negative, it will begin that many elements from the end of the array (with origin -1, meaning -n is the index of the nth last element and is therefore equivalent to the index of array.length - n). If array.length + start is less than 0, it will begin from index 0.



    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice

    关于javascript - 误解 `each...in` 用 react 变量渲染,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59248122/

    相关文章:

    javascript - 当另一个 div 展开时隐藏 div?

    javascript - Noodle.js 设置/jQuery 流程(网页抓取帮助!)

    javascript - Ajax 调用后的函数内的 document.ready()

    javascript - 如何关闭用 Javascript 加载后打开的窗口?

    javascript - 错误 : Failed to execute 'insertBefore' on 'Node'

    meteor - Meteor.call 内的集合不会更新服务器数据

    javascript - 每个客户端的 meteor 同步内容

    node.js - 基于间隔的 Meteor 更新模板元素

    javascript - meteor : Stripe Checkout error

    javascript - Meteor.js 中 UI.dynamic 模板的助手