我正在尝试制作一个行为与我开发的React Hook相同(正确)的React类组件。当页面首次加载时,我在stackblitz中具有的示例和此处显示的示例未显示正确的图像。一旦发生滚动事件,它将变为正确。
我有以下示例显示了错误的行为。通过将import语句更改为当前已注释掉的语句,可以看到正确的行为。当我从useEffect的依赖项数组中删除isLoading时,该钩子以相同的方式破坏。
https://stackblitz.com/edit/react-scroll-problem
注意:在构造函数中将isLoading更改为false可以解决问题,但是增加了钩子所没有的图像的双重显示(首先是黑白,然后是彩色)。
import * as React from "react";
class ImageToggleOnScrollCC extends React.Component {
constructor(props) {
super(props);
this.imgRef = React.createRef();
this.state = {
inView: false,
isLoading: true
};
}
isInView = imageRefx => {
if (this.imgRef.current) {
const rect = this.imgRef.current.getBoundingClientRect();
return rect.top >= 0 && rect.bottom <= window.innerHeight;
}
return false;
};
scrollHandler = () => {
this.setState({
inView: this.isInView()
});
};
// componentDidUpdate(prevProps) {
// console.log("componentDidUpdate")
// if (this.props.isLoading !== prevProps.isLoading) {
// console.log("componentDidUpdate isLoading changed")
// }
// }
componentWillUnmount() {
window.removeEventListener("scroll", scrollHandler);
}
componentDidMount() {
window.addEventListener("scroll", this.scrollHandler);
this.setState({
inView: this.isInView(),
isLoading: false
});
}
render() {
if (this.state.isLoading === true) {
return null;
} else {
return (
<div>
<i>ImageToggleOnScrollCC - Class Component</i>
<br />
<img
src={
this.state.inView
? 'https://via.placeholder.com/200x200.png/0000FF/808080?text=ON-SCREEN'
: 'https://via.placeholder.com/200x200.png?text=off-screen'
}
alt=""
ref={this.imgRef}
width="200"
height="200"
/>
</div>
);
}
}
}
export default ImageToggleOnScrollCC;
下面是正在工作的React Hook组件,我希望上面的Class组件能够正常工作。
import React, { useRef, useEffect, useState } from "react";
const ImageTogglerOnScroll = ({ primaryImg, secondaryImg }) => {
const imageRef = useRef(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
window.addEventListener("scroll", scrollHandler);
setInView(isInView());
setIsLoading(false);
return () => {
window.removeEventListener("scroll", scrollHandler);
};
}, [isLoading]);
const [inView, setInView] = useState(false);
const isInView = () => {
if (imageRef.current) {
const rect = imageRef.current.getBoundingClientRect();
return rect.top >= 0 && rect.bottom <= window.innerHeight;
}
return false;
};
const scrollHandler = () => {
setInView(() => {
return isInView();
});
};
return isLoading ? null : (
<div>
<i>ImageToggleOnScroll - Functional Component React Hooks</i>
<br />
<img
src={inView ? secondaryImg : primaryImg}
alt=""
ref={imageRef}
width="200"
height="200"
/>
</div>
);
};
export default ImageTogglerOnScroll;
答案 0 :(得分:1)
这里的问题是两个组件都需要至少渲染3次才能不仅显示图像,而且显示inView
时加载的正确图像。
isLoading
设置为true。isLoading
设置为true后,才渲染图像并分配对该元素的引用。img
元素,确定其位置并将其标记为inView
。在hook组件中,您有useEffect((), obj)
来检查obj
的任何更改(本例为[isLoading]
),然后在hook版本中触发该组件的重新渲染。 isLoading
更改并执行步骤2和3后,这将导致重新呈现。
同时,在类组件中,第三个步骤从未真正运行过(无需用户手动触发滚动事件):第一次渲染,然后在componentDidMount()
中将isLoading
设置为{{1 }}就这样。因为此时尚未设置图像参考,所以false
返回isInView()
,并且不再运行任何代码。此类组件未进行后续false
更改的检查。
这是在类组件中实现该检查的一种方法:
isLoading