[英]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: 到目前为止,我有以下内容:
ref
in the format of YYYYMM_start
or YYYYMM_end
. ref
中的格式YYYYMM_start
或YYYYMM_end
。 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
组件。
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
在这两个函数( componentDidMount
和componentDidUpdate
)中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
属性的新子元素,则这些子元素将不会在父componentDidMount
的componentDidMount
可见,而在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: 它会像这样:
componentDidMount
execute (somewhat surprising perhaps, but the parent isn't done mounting until all its children have mounted, which makes sense) componentDidMount
执行(也许有些令人惊讶,但是直到所有子级都挂载后,父级才完成挂载,这是有道理的) componentDidMount
executes componentDidMount
执行 setState
with some added children and some children already existing from last render setState
更新,其中添加了一些子级,并且某些子级上次渲染已存在 componentDidMount
execute and all previously existing childrens' componentDidUpdate
execute componentDidMount
和执行所有以前存在的子级的componentDidUpdate
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
在其生命周期回调中处理自己的位置( componentDidMount
和componentDidUpdate
),而是让其父级组件( 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.