gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-11-30 15:10:34 24 4
gpt4 key购买 nike

真的很抱歉这个奇怪的问题。我正在寻找一个带有反应 Action 的短 Spring 动画。请耐心等待:

我有一个组件,它根据选定的基本目录在框架内加载本地目录和文件的列表,有点像 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在这种情况下,稍微滚动一下就可以自然浏览。

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

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

最佳答案

这是一个非常有趣的问题,先有鸡还是先有蛋,你如何测量需要渲染的东西的高度以便测量它,而不让用户首先看到它闪现。糟糕的解决方案可能涉及在屏幕外渲染某些东西并对其进行测量,但这与 React 提倡的概念不相符。

但是,不仅可以通过了解码件生命周期,还可以通过了解它如何适应浏览器生命周期来解决这个问题。

关键是 componentDidMountcomponentDidUpdate。一个常见的误解是这些发生在组件呈现给浏览器之后,这是不正确的,它会在发生之前触发

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

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

然后它将执行对真实 DOM 的派生更新(有效地呈现它到浏览器 DOM)。至关重要的是,虽然浏览器生命周期意味着这个渲染的 DOM 还没有被绘制到屏幕上(即从用户的 Angular 来看它不是 rendered),直到下一个 tick 时 requestAnimationFrame有机会拦截它...

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

我们还需要一个未在其上设置样式的可测量父元素,并存储一个 ref 以便我们可以在 DOM 节点处对其进行测量。以及一个高度动画的包装器。

没有 react-motion 的简化(但外部 div 可以包裹在 Motion 中,并且可以将插值插入到动态样式 prop 中)...

  // 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 包( https://github.com/nkbt/react-collapse )它使用 react-motion 在表面之下来做到这一点,并且还处理许多边缘情况,如嵌套动态高度等

但在你这样做之前,我强烈建议你至少自己尝试一下,因为它可以让你对 React 和浏览器生命周期有更深入的了解,并有助于获得你很难实现的深度交互体验否则

关于javascript - 如何从父组件中的子元素获取累积高度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45171865/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com