如何封装实现同步滚动的React ScrollDivs可复用组件?
How to Build a Reusable Synchronized Scroll Component in React
Great question! Let's turn your existing synchronized scroll logic into a clean, reusable ScrollDivs component that hides all the implementation details from the user. Here's a modern, function-component-based solution using React hooks:
The Reusable ScrollDivs Component
import React, { useRef } from 'react'; const ScrollDivs = ({ children }) => { // Store references to all child div elements const divRefs = useRef([]); // Handle scroll sync logic const handleScroll = (e) => { const { scrollLeft, scrollTop } = e.target; // Sync scroll position across all divs, skip the scrolled div itself to avoid redundant updates divRefs.current.forEach(ref => { if (ref && ref !== e.target) { ref.scrollLeft = scrollLeft; ref.scrollTop = scrollTop; } }); }; // Process each child to add ref and scroll handler const processedChildren = React.Children.map(children, (child, index) => { // Only modify div elements (adjust this check if you need to support other scrollable elements) if (child.type === 'div') { // Merge user's existing onScroll with our sync handler if present const combinedOnScroll = child.props.onScroll ? (e) => { child.props.onScroll(e); handleScroll(e); } : handleScroll; return React.cloneElement(child, { ref: (el) => (divRefs.current[index] = el), onScroll: combinedOnScroll }); } // Return non-div children unchanged return child; }); return <div>{processedChildren}</div>; }; export default ScrollDivs;
Key Details Explained
divRefs: UsesuseRefto maintain an array of DOM references to all the child divs we want to sync. This persists across component re-renders without triggering unnecessary state updates.handleScroll: When any div is scrolled, it captures the current scroll position and applies that position to all other divs in the ref array. We skip the target div itself to avoid redundant DOM operations.- Child Processing: Uses
React.Children.mapandReact.cloneElementto safely modify each child div:- Adds a ref to store the DOM element in our
divRefsarray - Attaches the sync scroll handler, and merges any existing
onScrollprop from the user so their custom logic still runs alongside the sync functionality
- Adds a ref to store the DOM element in our
How to Use the Component
Users can now wrap any number of scrollable divs inside <ScrollDivs> without worrying about the underlying sync logic:
import ScrollDivs from './ScrollDivs'; // Example usage const App = () => { return ( <div style={{ border: '1px solid tomato', padding: '8px' }}> <ScrollDivs> <div style={{ width: 300, height: 100, overflow: 'auto', border: '1px solid black', margin: '2px 0' }}> <div style={{ width: 500, height: 400 }}> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Scelerisque eu ultrices vitae auctor eu augue ut lectus. In fermentum et sollicitudin ac orci. </div> </div> <div style={{ width: 300, height: 100, overflow: 'auto', border: '1px solid black', margin: '2px 0' }}> <div style={{ width: 500, height: 400 }}> Velit sed ullamcorper morbi tincidunt ornare. Auctor eu augue ut lectus arcu bibendum. Non nisi est sit amet. Facilisis magna etiam tempor orci eu lobortis. Et tortor at risus viverra adipiscing at in tellus integer. </div> </div> <div style={{ width: 300, height: 100, overflow: 'auto', border: '1px solid black', margin: '2px 0' }}> <div style={{ width: 500, height: 400 }}> Lacus luctus accumsan tortor posuere ac ut consequat semper viverra. Fermentum dui faucibus in ornare quam viverra orci sagittis. Porttitor eget dolor morbi non. Pulvinar pellentesque habitant morbi tristique senectus et. </div> </div> </ScrollDivs> </div> ); }; export default App;
Bonus: Class Component Version (For Legacy Codebases)
If you need a class-based implementation for older React projects, here's how you'd structure it:
import React from 'react'; class ScrollDivs extends React.Component { constructor(props) { super(props); this.divRefs = []; } handleScroll = (e) => { const { scrollLeft, scrollTop } = e.target; this.divRefs.forEach(ref => { if (ref && ref !== e.target) { ref.scrollLeft = scrollLeft; ref.scrollTop = scrollTop; } }); }; render() { const processedChildren = React.Children.map(this.props.children, (child, index) => { if (child.type === 'div') { const combinedOnScroll = child.props.onScroll ? (e) => { child.props.onScroll(e); this.handleScroll(e); } : this.handleScroll; return React.cloneElement(child, { ref: (el) => (this.divRefs[index] = el), onScroll: combinedOnScroll }); } return child; }); return <div>{processedChildren}</div>; } } export default ScrollDivs;
This component is flexible, user-friendly, and abstracts all the sync complexity away—exactly what you're looking for!
内容的提问来源于stack exchange,提问作者user6456564
相关产品推荐
相关产品推荐

