现在需要创建一个Block组件,需要根据scroll value来控制其显示的位置。
const MainScrollableArea = () => {
const [position, setPosition] = useState(300);
const onScroll = (e) => {
// calculate position based on the scrolled value
const calculated = getPosition(e.target.scrollTop);
// save it to state
setPosition(calculated);
};
return (
<div className="scrollable-block" onScroll={onScroll}>
{/* pass position value to the new movable component */}
<MovingBlock position={position} />
<VerySlowComponent />
<BunchOfStuff />
<OtherStuffAlsoComplicated />
</div>
);
根据 React Rerender-Move State Down模式我们了解到这会导致整个MainScrollableArea
都会重新渲染。
于是我们可以将不依赖于position
的组件提取出来。
const ScrollableWithMovingBlock = ({children}) => {
const [position, setPosition] = useState(300);
const onScroll = (e) => {
const calculated = getPosition(e.target.scrollTop);
setPosition(calculated);
};
return (
<div className="scrollable-block" onScroll={onScroll}>
<MovingBlock position={position} />
{/* slow bunch of stuff used to be here, but not anymore */}
{children}
</div>
);
};
const App = () => {
return (
<ScrollableWithMovingBlock>
<VerySlowComponent />
<BunchOfStuff />
<OtherStuffAlsoComplicated />
</ScrollableWithMovingBlock>
);
};
这样VerySlowComponent
、BunchOfStuff
、OtherStuffAlsoComplicated
这些组件就不会重新渲染了。有人可能会疑惑,为什么这样就不会重新渲染了呢?VerySlowComponent
、BunchOfStuff
、OtherStuffAlsoComplicated
这些组件仍然在ScrollableWithMovingBlock
中,position
发生改变了,为啥它们不会重新渲染呢?
这就需要了解Rerender的机制了。Rerender的时候,React会重新执行创建组件的函数,然后通过Object.is来判断是否有变化来判断是否重新创建。以上面的ScrollableWithMovingBlock
为例,看看Rerender的过程。
const ScrollableWithMovingBlock = ({children}) => {
const [position, setPosition] = useState(300);
const onScroll = (e) => {
const calculated = getPosition(e.target.scrollTop);
setPosition(calculated);
};
return (
<div className="scrollable-block" onScroll={onScroll}>
<MovingBlock position={position} />
{/* slow bunch of stuff used to be here, but not anymore */}
{children}
</div>
);
};
当position
发生变化的时候,会重新执行ScrollableWithMovingBlock
函数,判断其返回值中所有的Object是否有变化。MovingBlock
是在本地创建的,所以每次都是新创建的,Object.is
判断为false。所有MovingBlock
会重新创建。对于children
由于是外部创建的,所以Object.is
判断为true,children不会重新渲染。