简体   繁体   English

引用安装/更新组件的生命周期

[英]Refs in mount/update component lifecycle

I have an infinite scrolling list that scrolls in both directions. 我有一个无限的滚动列表,可以双向滚动。 The list presents a 5 item-per-row grid that represents the 5 work days in a 7 day week. 该列表显示了每行5个项目的网格,该网格代表一周7天中的5个工作日。 The days are zebra stripped by month (even months have a slightly darker color). 斑马按天剥离斑马纹(甚至几个月的颜色略深)。 I want to position the month title in a column to the left of the grid, starting at the first day of the month, or the earliest day available for the month (given the AJAXed in data -- does not always start with the first weekday of the month). 我想将月份标题放置在网格左侧的一列中,从该月的第一天开始,或者该月可用的最早的一天(考虑到数据中的AJAX,但并不总是从第一个工作日开始)的月份)。

I have all the necessary logic in place to figure out which day is the first day (as I'm only showing weekdays, if the first falls on a weekend, then the start of the month might not be the first). 我拥有所有必要的逻辑来确定哪一天是第一天(因为我只显示工作日,如果第一天是周末,那么月初可能不是第一天)。

I have the following so far: 到目前为止,我有以下内容:

  1. When rendering the grid, tag the beginning of the month and the end of the month with a ref in the format of YYYYMM_start or YYYYMM_end . 当渲染网格,吊牌月初和月中的一个最终ref中的格式YYYYMM_startYYYYMM_end
  2. I then render the MonthTitle component with a monthStamp prop that is in the same format of YYYYMM , as well as a findMonthNode prop that receives a function to lookup the start and end node on the parent component (using regex). 然后,我使用具有与monthStamp相同格式的YYYYMM以及一个findMonthNode (使用正则表达式)来接收在父组件上查找开始和结束节点的函数来呈现MonthTitle组件。

  3. In the MonthTitle component, in componentDidMount , I can then position the title absolutely relative to the start node for that month. 然后,在MonthTitle组件的componentDidMount ,可以将标题绝对相对于该月的起始节点定位。 I also invoke the exact same function in componentDidUpdate . 我还调用了componentDidUpdate完全相同的函数。

My issue is when I scroll the list (remember, infinite scroll in both directions) back in time: my AJAX fires and loads in the new tiles with the new month titles, and the new month titles render appropriately, but the first of the original titles does NOT update accordingly. 我的问题是,当我向后滚动列表时(请记住,在两个方向上都可以无限滚动):我的AJAX会触发并加载带有新月份标题的新图块,并且新月份标题会适当地呈现,但是是原始的第一个标题不会相应更新。 After some poking around, I realized that this.refs in the parent component in componentDidUpdate does not equal this.refs in the parent component in componentDidMount . 一些闲逛之后,我意识到, this.refs在父组件componentDidUpdate 不等于 this.refs在父组件componentDidMount componentDidMount seems to have the complete list, whereas componentDidUpdate only has the list of nodes that existed before the new nodes (from the AJAX response) have been mounted. componentDidMount似乎具有完整的列表,而componentDidUpdate仅具有在挂载新节点(来自AJAX响应)之前存在的节点的列表。

Since componentDidUpdate does not have all the new refs, I cannot position the previously first month title accordingly, and it remains stuck where it was initially rendered in componentDidMount , which--given the new data--is now the incorrect position. 由于componentDidUpdate并没有所有新的引用,因此我无法相应地放置先前的第一个月标题,并且它仍然停留在componentDidMount中最初呈现的位置(鉴于新数据)现在是不正确的位置。


Why is this.refs different in these two functions ( componentDidMount & componentDidUpdate )? 为什么this.refs在这两个函数( componentDidMountcomponentDidUpdate )中this.refs不同? What can I do to ensure that I have access to the complete this.refs when mounting AND updating components? 在安装和更新组件时,如何确保可以访问完整的this.refs

Using react 0.13.3 FYI 使用react 0.13.3 FYI

If you look at the component lifecycle specs here , you will see that componentDidUpdate is run every time your component has been updated (via this.setState in either the component or one of its ancestors), but not after the initial render: 如果您在此处查看组件生命周期规格,您将看到每次更新componentDidUpdate都会运行componentDidUpdate(通过组件或其祖先之一中的this.setState ),而不是在初始渲染之后运行:

componentDidUpdate(object prevProps, object prevState)

Invoked immediately after the component's updates are flushed to the DOM. 组件的更新刷新到DOM后立即调用。 This method is not called for the initial render. 初始渲染不调用此方法。

componentDidMount on the other hand is run after the initial render, but not after updates via setState : 另一方面, componentDidMount在初始渲染后运行,但在通过setState更新后不运行:

componentDidMount()

Invoked once, only on the client (not on the server), immediately after the initial rendering occurs. 初始渲染发生后立即仅在客户端(而不是服务器)上调用一次。

So if you update your state (via this.setState in your component) and re-render your component, potentially adding new child elements that have ref attributes, those will not be visible in the parent's componentDidMount but in componentDidUpdate . 因此,如果您更新状态(通过组件中的this.setState )并重新呈现组件,并有可能添加具有ref属性的新子元素,则这些子元素将不会在父componentDidMountcomponentDidMount可见,而在componentDidUpdate可见。

On the other hand, if you update your parent component and the update renders new child components, those child components' componentDidMount will indeed fire before the parent's componentDidUpdate as the parent component is not done updating until all its new child components have mounted and rendered properly. 另一方面,如果您更新父组件并且更新呈现新的子组件,则这些子组件的componentDidMount实际上会在父组件的componentDidUpdate之前触发,因为父组件在所有新的子组件均已正确安装并正确渲染之前尚未完成更新。 It will go like this: 它会像这样:

  1. Any initial childrens' componentDidMount execute (somewhat surprising perhaps, but the parent isn't done mounting until all its children have mounted, which makes sense) 任何初始子级的componentDidMount执行(也许有些令人惊讶,但是直到所有子级都挂载后,父级才完成挂载,这是有道理的)
  2. Parent's componentDidMount executes 父母的componentDidMount执行
  3. Parent updates via setState with some added children and some children already existing from last render 父级通过setState更新,其中添加了一些子级,并且某些子级上次渲染已存在
  4. All newly added childrens' componentDidMount execute and all previously existing childrens' componentDidUpdate execute 执行所有新添加的子级的componentDidMount和执行所有以前存在的子级的componentDidUpdate
  5. Parent's componentDidUpdate executes 父母的componentDidUpdate执行

Here's a JSFiddle demonstrating this. 这是JSFiddle演示的。

Ok, I figured out a solution. 好的,我想出了一个解决方案。 Instead of MonthTitle handling its own position during its lifecycle callbacks ( componentDidMount and componentDidUpdate ), I am having their parent component ( Grid ) invoke methods on the titles to position them in the parent's componentDidUpdate . 我没有让MonthTitle在其生命周期回调中处理自己的位置( componentDidMountcomponentDidUpdate ),而是让其父级组件( Grid )调用标题上的方法以将其放置在父级的componentDidUpdate

So, roughly, in Grid , I have the following: 因此,大致而言,在Grid ,我具有以下内容:

componentDidUpdate() {
  for (let [ref, component] of entries(this.refs)) {
    if (/_title$/.test(ref)) {
      var startOfMonthNode = React.findDOMNode(findRef("startOfMonth", this.refs, component.props.monthStamp))
      var endOfMonthNode = React.findDOMNode(findRef("endOfMonth", this.refs, component.props.monthStamp))

      component.position(startOfMonthNode, endOfMonthNode)
    }
  }
}

Then, inside my MonthTitle component, I define the positioning logic: 然后,在我的MonthTitle组件中,定义定位逻辑:

position(startOfMonthNode, endOfMonthNode) {
  React.findDOMNode(this).style["top"] = getDimension((startOfMonthNode || endOfMonthNode), "top") + window.scrollY - 116 + "px"
}

This means that I had to add the titles themselves to my refs list in Grid . 这意味着我必须将标题本身添加到Grid refs列表中。


So, conceptually, the difference was moving when the positioning updates happened from the child component's lifecycle to the parent component's lifecycle. 因此,从概念上讲, 位置更新从子组件的生命周期到父组件的生命周期发生变化 ,差异就在转移。 Since the parent component's componentDidUpdate will fire after any new child components have been mounted, I have the complete list of refs available. 由于父组件的componentDidUpdate将在安装任何新的子组件后触发,因此我有完整的refs列表。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM