如何从父组件中的子元素获取累积高度

时间:2017-07-18 15:59:36

标签: javascript reactjs react-motion

对这个奇怪的问题非常抱歉。 我期待用反应动作创建一个短弹簧动画。 请耐心等待:

我有一个组件,根据选定的基本目录,加载框架内的本地目录和文件列表,有点像mac finder窗口。因此,根据任意目录,获取的文件数量会有所不同,因此我的帧的高度也会变化(直到达到最大高度)。

现在,只要获取了所选目录的内容,我就想要动态地将此组件的框架打开到正确的高度(或最大高度)。如果我要更改目录,我需要根据提取的元素数量或直接到最大高度来弹出/调整帧高度。

以下是该组件的相关摘录:

  <Motion
    defaultStyle={{x: 30}}
    style={{x: spring(330, {stiffness: 270, damping: 17})}}
  >
    {({x}) =>  
      <List height={x}>
        {
          localTree.map(item =>
            <ListItem key={v4()}>
              <FileIcon isDirectory={item.isDirectory} />
              <span onClick={() => this.handleClick(item.id)}>
                {item.baseName}
              </span>
            </ListItem>
          )
        }
      </List>
    }
  </Motion>

您可以看到,目前defaultStylestyle变量是静态设置的。它运行正常,特别是因为我已将<List />组件的样式设置为overflow: scroll,因此如果在这种情况下有更多元素而不是330px,则稍微滚动会使其自然可浏览。 / p>

但这不是我最终想要的。 我希望defaultStyle={{x: 30}}style={{x: spring(330, )}}由累积的<ListItem />子元素数的高度动态设置。

我该如何解决这个问题?我应该在某处添加父组件吗?

1 个答案:

答案 0 :(得分:2)

这是一个非常有趣的问题,非常鸡肉和鸡蛋,你如何测量需要渲染的东西的高度,以便测量它,而不会让用户首先看到它闪烁。对此的不良解决方案可能涉及将某些内容从屏幕上移除并进行测量,但这与React推广的概念并不相符。

然而,不仅可以通过了解组件生命周期,还可以了解它如何适应浏览器生命周期来解决。

关键是componentDidMountcomponentDidUpdate。一个常见的误解是,这些是在组件被渲染到浏览器之后发生的,而不是真的,它会在发生之前触发

render将首先将虚拟DOM与您要呈现的任何内容进行协调(此处的另一个误解 - 虚拟DOM实际上不是真实的甚至是影子DOM,它只是表示组件的JS对象树和算法可以导出差异的DOM - 你无法确定动态高度或此时与真实DOM元素交互。)

如果它感觉到要进行实际的DOM突变,那么componentDidMount / componentDidUpdate现在会在实际的浏览器渲染之前触发。

然后它将执行对真实DOM的派生更新(有效rendering它到浏览器DOM)。至关重要的是,浏览器生命周期意味着这个渲染的DOM还没有被绘制到屏幕上(即从用户的角度看它不是rendered),直到requestAnimationFramecomponentWillReceiveProps | V shouldComponentUpdate | V componentWillUpdate | V render | V componentDidUpdate | V [BROWSER DOM rendered - can be interacted with programatically] | V requestAnimationFrame callback | V [BROWSER screen painted with updated DOM - can be seen and interacted by user] 时的下一个勾号有机会拦截它......

componentDidX

所以在window.requestAnimationFrame的这一点上,我们有机会告诉它在绘制之前拦截浏览器,但我们可以与真正更新的DOM进行交互并执行测量!为此,我们需要使用componentWillUnmount,它采用回调函数,我们可以在其中执行测量。由于这是一个异步函数,它返回一个id,您可以使用它来取消它,就像setTimeout一样(我们应该记得从ref清除所有待处理的请求)。

我们还需要一个可测量的父元素,它不会在其上设置样式,并存储div,因此我们可以到DOM节点进行测量。还有一个高度动画的包装。

简化而没有反应运动(但外部Motion可以包裹在 // added type info for better clarity constructor(props: SomeProps) { super(props) this.state = { height: undefined } } listRef: HTMLElement bindListRef = (el: HTMLElement): void => { this.listRef = el } rafId: number interceptAndMeasure = (): void { // use request animation frame to intercept and measure new DOM before the user sees it this.rafId = window.requestAnimationFrame(this.onRaf) } onRaf = (): void => { const height: number = this.listRef.getBoundingClientRect().height // if we store this in the state - the screen will not get painted, as a new complete render lifecycle will ensue // BEWARE you must have a guard condition against setting state unnecessarily here // as it is triggered from within `componentDidX` which can if unchecked cause infinite re-render loops! if (height !== this.state.height) this.setState({height}) } render() { const {data} = this.props const {height} = this.state.height // the ul will be pushed out to the height of its children, and we store a ref that we can measure it by // the wrapper div can have its height dynamically set to the measured value (or an interpolated interim animated value!) return ( <div style={height !== undefined ? {height: `${height}px`} : {}}> <ul ref={this.bindListRef}> {data.map((_, i) => <li key={i}> {_.someMassiveText} </li> )} </ul> </div> ) } componentDidMount() { this.interceptAndMeasure() } componentDidUpdate() { this.interceptAndMeasure() } componentWillUnmount() { // clean up in case unmounted while still pending window.cancelAnimationFrame(this.rafId) } 中并且可以将插值插入动态样式道具中... ...

react-collapse

当然以前遇到过这种情况,幸运的是,你可以不必担心自己实现它并使用使用react-motion的优秀newmerge.replace([np.var=='', np.nan], [True, False]) Company Weight IsinSP100? Symbol AAPL Apple Inc. 3.699828 AAPL MSFT Microsoft 2.686835 MSFT AMZN Amazon.com Inc. 1.901109 AMZN AN AutoNation Inc. 0.013148 False NWS News Corporation 0.007284 False 包(https://github.com/nkbt/react-collapse)在表面下面做这个,并且还处理许多边缘情况,如嵌套动态高度等

但在此之前,我真的恳请您至少自己尝试一下,因为它可以更深入地了解React和浏览器生命周期,并将有助于您将很难实现的深度互动体验否则