React中的onScroll
相当于什么?我知道我应该使用Refs,但是我如何观察多个Refs <ul>
<li data-position="below-viewport"></li>
<li data-position="below-viewport"></li>
<li data-position="below-viewport"></li>
<li data-position="below-viewport"></li>
</ul>
?
我通常使用类似下面的函数来检查页面中多个元素的视口位置,并在每个元素进入视口时触发不同的css动画:
HTML
getPosition: function (element) {
const rect = element.getBoundingClientRect();
if ((rect.top > -1) && (rect.top < (window.innerHeight * 0.75))) {
element.setAttribute('data-js-position','in-viewport');
} else if ((rect.top > 0) && (rect.top < window.innerHeight)) {
element.setAttribute('data-js-position','entering-viewport');
} else if (rect.top > window.innerHeight) {
element.setAttribute('data-js-position','below-viewport');
} else if (rect.bottom < 0) {
element.setAttribute('data-js-position','above-viewport');
}
}
window.addEventListener('scroll', function() {
[].forEach.call(document.querySelectorAll('[data-js-position]'), el => {
positionChecker.getPosition(el);
})
});
的Javascript
App.js
我如何在React中实现类似的东西?你能给我一个在React中观察多个div的函数的例子吗?
更好:我如何在{{1}}中抽象这个函数,以便我也可以在子组件中使用它?
答案 0 :(得分:3)
使每个li
html元素成为自己的组件,并在其自己的状态下保存ref
个引用。
class LiElement extends React.Component {
componentDidMount() {
this.ref.getPosition()
}
render() {
return (
<li ref={(ctx) => this.ref = ctx}>
</li>
)
}
}
答案 1 :(得分:0)
通过refs迭代可能会让你得到类似于你想要实现的东西。
在这个例子中,我将每个节点存储在本地Map中,这样你就可以遍历它们并得到它们的getBoundingClientRect。
注意,有几种方法可以做到这一点,你不必创建一个Map,你可以只获得每个元素的“ref”,但是你必须给它分配一个不同的“ref”值每个“li”。
此外,最好限制handleScroll调用。。
class App extends React.Component {
constructor(props) {
super(props);
this._nodes = new Map();
}
componentDidMount() {
window.addEventListener("scroll", this.handleScroll);
}
handleScroll = e => {
Array.from(this._nodes.values())
// make sure it exists
.filter(node => node != null)
.forEach(node => {
this.getPosition(node);
});
};
getPosition = node => {
const rect = node.getBoundingClientRect();
// do something cool here
console.log("rect:", rect);
};
componentWillUnmount() {
window.removeEventListener("scroll", this.handleScroll);
}
render() {
return (
<div>
<ul>
<li key="1" ref={c => this._nodes.set(1, c)}>
Your thing 1
</li>
<li key="2" ref={c => this._nodes.set(2, c)}>
Your thing 2
</li>
<li key="3" ref={c => this._nodes.set(3, c)}>
Your thing 3
</li>
</ul>
</div>
);
}
}
答案 2 :(得分:0)
添加其他可能的解决方案:创建一个新的反应组件VisibilitySensor
,以便只使用一个ref和一个函数
import React, { Component } from 'react';
class VisibilitySensor extends Component {
componentDidMount() {
window.addEventListener('scroll', this.getElementPosition = this.getElementPosition.bind(this));
this.getElementPosition();
}
componentWillUnmount() {
window.removeEventListener('scroll', this.getElementPosition);
}
getElementPosition() {
const element = this.visibilitySensor;
var rect = element.getBoundingClientRect();
if ((rect.top > 0) && (rect.top < (window.innerHeight * 0.75))) {
element.setAttribute("data-position","in-viewport");
} else if (rect.top > window.innerHeight) {
element.setAttribute("data-position","below-viewport");
} else if (rect.bottom < 0) {
element.setAttribute("data-position","above-viewport");
}
}
render() {
return (
<div data-position="below-viewport" ref={(element) => { this.visibilitySensor = element; }}>
{this.props.children}
</div>
);
}
}
export default VisibilitySensor;
然后包装每个li
(或div
)我需要注意上面的组件。我个人最终不喜欢上面的解决方案,因为新添加的div
会破坏我的CSS样式,特别是与父级宽度相关的子宽度等。
<ul>
<VisibilitySensor>
<li></li>
</VisibilitySensor>
<VisibilitySensor>
<li></li>
</VisibilitySensor>
<VisibilitySensor>
<li></li>
</VisibilitySensor>
<VisibilitySensor>
<li></li>
</VisibilitySensor>
</ul>