![](/img/trans.png)
[英]For better performance, which is better: componentWillUpdate or componentDidUpdate?
[英]Bubbling componentWillUpdate and componentDidUpdate
我知道這不是慣用的React,但我正在研究一個React組件(反向滾動視圖),它需要在渲染之前和渲染之后獲得下游虛擬DOM更改的通知。
這有點難以解釋所以我創建了一個JSFiddle起點: https ://jsfiddle.net/7hwtxdap/2/
我的目標是讓日志看起來像:
Begin log
render
rendered small
rendered small
rendered small
before update
rendered big
after update
before update
rendered big
after update
before update
rendered big
after update
我知道React有時批處理DOM更改,這很好(即日志事件可以before update; rendered big; rendered big; after update; ...
) - 重要的部分是我實際上在通知之前和之后通知DOM改變了。
我可以通過指定回調來手動近似行為,如下所示: https : //jsfiddle.net/7hwtxdap/4/ 。 然而,這種方法並不具有規模 - 例如,我需要讓后代而不是兒童發生事件; 而且不想在我使用的每個組件上添加這些事件處理程序。
使用案例:
我正在為消息制作一個“反向滾動組件”(其中,當添加新元素或現有元素改變大小時,滾動更改的基礎是從內容的底部而不是每個消息傳遞應用程序的頂部ala)具有動態修改的子項(類似於<MyElement />
類但具有可變高度,來自ajax的數據等)。 這些不是從類似Flux的狀態存儲中獲取數據,而是使用pub-sub數據同步模型(與setTimeout()非常接近)。
要使反向滾動工作,您需要執行以下操作:
anyChildrenOfComponentWillUpdate() {
let m = this.refs.x;
this.scrollBottom = m.scrollHeight - m.scrollTop - m.offsetHeight;
}
anyChildrenOfComponentDidUpdate() {
let m = this.refs.x;
m.scrollTop = m.scrollHeight - m.offsetHeight - this.scrollBottom;
}
重要的是,對於這種設計,我希望能夠將元素包裝在不是“反向滾動感知”的其他元素周圍(即,不必具有特殊代碼來通知反向滾動處理程序它們已經改變)。
根據設計,React會認為組件上的setState
調用應該觸發that
組件的重新呈現。 我所知道的關於組件生命周期的最可靠方法是組件本身。
如何將notifier()
回調作為prop傳遞,然后在子組件的componentWillMount
和componentDidUpdate
生命周期鈎子中調用此函數?
這是一個有效的js小提琴這個想法。 讓我知道會發生什么。
編輯1
如果您不想在每個子組件中來回傳遞回調,那么我會說您只是遇到了Redux
和Flux
的特定用例。
使用Redux,組件可以通過reducer功能更改商店的狀態。 然后重新呈現在該商店中觀察變化的每個組件。 Flux
使用相同的想法。
所以我建議你使用redux來處理狀態設置。 要開始,您可以通過redux的創建者Dan Abramov閱讀這些教程視頻 。
在不知道為什么需要此功能的情況下,很難知道問題的最佳解決方案是什么。 但是,我將解決這一具體問題:
例如,我需要從后代而不是孩子那里冒泡; 而且不想在我使用的每個組件上添加這些事件處理程序。
我建議對此解決方案進行兩處更改。 第一種是考慮使用上下文 ,但在決定之前一定要閱讀警告。 這將允許您在單個組件中定義回調,並在所有后代中立即使用它們。
第二個更改是將所有日志記錄邏輯移動到單獨的組件中,並讓其他組件根據需要繼承它。 該組件看起來像:
class LoggingComponent extends React.Component {
componentWillUpdate() {
if (this.context.onWillUpdate) {
this.context.onWillUpdate();
}
}
componentDidUpdate() {
if (this.context.onDidUpdate) {
this.context.onDidUpdate();
}
}
}
LoggingComponent.contextTypes = {
onWillUpdate: React.PropTypes.func,
onDidUpdate: React.PropTypes.func
};
這樣,您可以輕松地將此功能添加到新組件,而無需重復邏輯。 我用一個工作演示更新了你的jsfiddle 。
正如James所說,您可以使用上下文來定義一個回調函數,您可以將其傳遞給所有后代組件,如果您不想擴展組件,可以使用裝飾器將高階組件包裝在它們周圍。 像這樣的東西:
在你的容器中:
class Container extends React.Component {
static childContextTypes = {
log: React.PropTypes.func.isRequired
};
getChildContext() {
return {
log: (msg) => {
console.log(msg)
}
}
}
render() {
console.log("Begin Log")
return (
<div>
<MyElement />
<MyElement />
<MyElement />
</div>
);
}
}
裝飾者類:
export const Logger = ComposedComponent => class extends React.Component {
static contextTypes = {
log: React.PropTypes.func.isRequired
};
constructor(props) {
super(props)
}
componentWillReceiveProps(nextProps) {
this.context.log('will receive props');
}
componentWillUpdate() {
this.context.log('before update');
}
componentDidUpdate() {
this.context.log('after update');
}
render() {
return <ComposedComponent {...this.props}/>
}
}
然后裝飾你想要的組件
@Logger
class MyElement extends React.Component {
...
}
也許你可以看看react-virtualized ,它有一個反向滾動列表的例子。
export default class Example extends Component { constructor (props) { super(props) this.state = { list: [] } } componentDidMount () { this._interval = setInterval(::this._updateFeed, 500) } componentWillUnmount () { clearInterval(this._interval) } render () { const { list } = this.state return ( <div className={styles.VirtualScrollExample}> <VirtualScroll ref='VirtualScroll' className={styles.VirtualScroll} width={300} height={200} rowHeight={60} rowCount={list.length} rowRenderer={::this._rowRenderer} /> </div> ) } _updateFeed () { const list = [ ...this.state.list ] list.unshift( // Add new item here ) this.setState({ list }) // If you want to scroll to the top you can do it like this this.refs.VirtualScroll.scrollToRow(0) } _rowRenderer (index) { return ( // Your markup goes here ) } }
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.