How can I synchronize the "scrollLeft" property of 2 divs while scrolling horizontally within a react component? - javascript

I would like to "synchronize" the x-position of 2 "divs" within a react component. Eventually, I would like to have a table header that stays always visible and a table that can scroll vertically. The horizontal offset of that table header and that table are supposed to remain "in sync".
The "onScroll" event fires. However, changing the "state" of the property "offsetX" in my function reactToScrolling has no effect on my "divs" (as far as I can see). What can I do to make this work?
const { useState } = require('react');
const MainComponent = () => {
const [ offsetX, setOffsetX ] = useState(0);
function reactToScrolling(e) {
console.log(e.target.scrollLeft);
setOffsetX(e.target.scrollLeft);
}
return (
<>
<div style={{ height:'200pt', width:'800pt', overflow:'scroll'}} onScroll={reactToScrolling}>
<div style={{ height:'600pt', width:'1600pt', backgroundColor:'red' }} scrollLeft={offsetX}>
...
</div>
</div>
<div style={{ height:'200pt', width:'800pt', overflow:'scroll'}} onScroll={reactToScrolling}>
<div style={{ height:'600pt', width:'1600pt', backgroundColor:'blue' }} scrollLeft={offsetX}>
...
</div>
</div>
</>
)
};
export default MainComponent;

Eventually, I found the solution myself. It works perfectly if useRef is used instead of useState. When the scroll event fires, the scrollLeft property of div1 is set to the value of the scrollLeft property of div2 using the references created with useRef.
const { useRef } = require('react');
const MainComponent = () => {
const div1 = useRef(null);
const div2 = useRef(null);
const onScroll = () => {
div1.current.scrollLeft = div2.current.scrollLeft;
}
return (
<>
<div ref={div1} style={{ height:'200pt', width:'800pt', overflow:'scroll'}} onScroll={onScroll}>
<div style={{ height:'600pt', width:'1600pt', backgroundColor:'lightgray' }}>
...
</div>
</div>
<div ref={div2} style={{ height:'200pt', width:'800pt', overflow:'scroll'}} onScroll={onScroll}>
<div style={{ height:'600pt', width:'1600pt', backgroundColor:'lightgray' }}>
...
</div>
</div>
</>
)
};
export default MainComponent;

Thanks, this helped me a lot.
For a final touch, I just added another function for the top div so that it can handle the scrolling itself too.
const onScrollTop = () => {
div2.current.scrollLeft = div1.current.scrollLeft;
}

Related

Performance cost when using custom hooks to detect element height React

The common problem of having a position: fixed header and applying consistent padding to the content below it so that nothing is covered. If the content inside of the header is dynamically subject to changing and the styling needs to stay consistent regardless of what is added or removed.
I implemented this hook:
export const useElemHeight = () => {
const elementRef = useRef(null);
const [heightOfEl, setHeightOfEl] = useState(null);
function handleCheckElHeightOnScreenResize() {
setHeightOfEl(elementRef.current.clientHeight);
}
useEffect(() => {
if (elementRef.current) {
setHeightOfEl(elementRef.current.clientHeight);
window.addEventListener("resize", handleCheckElHeightOnScreenResize);
return () => {
window.removeEventListener("resize", handleCheckElHeightOnScreenResize);
};
}
}, [elementRef]);
return [elementRef, heightOfEl];
};
Application
export default function App() {
const [elementRef, heightOfEl] = useElemHeight();
return (
<div className="App">
<Header ref={elementRef} />
<Content height={heightOfEl} />
</div>
);
}
const Content = ({ height }) => {
const ADDITIONAL_UNITS = 20;
return (
<div
className="content"
style={{ paddingTop: `${height + ADDITIONAL_UNITS}px` }}
>
CONTENT
</div>
);
};
export default Content;
regardless of what content is added or removed from the header the padding will always stay consistent via all screen sizes, but are there any real performance costs to implementing something like this? Working demo here.
Sandbox

How to set parent node style in React?

I try to set parent node background color like this, but does not work, why?
<div
onLoad="this.parentNode.style.background = 'yellow';"
>
Found here the inspiration: https://www.w3schools.com/jsref/prop_node_parentnode.asp
The main challange here is this: Make absolute positioned div expand parent div height
I have a parent div with position relative and a child div with position absolute, and I would set the parent height the same as the child.
This post above tells, it can be done only with Javascript, but there is no exact steps for it. Would you help? And I have React on top of it.
I thought if I can set color, I will able to set height also. Set color seemed a bit easier in first turn.
Why don't you use useRef hook to get the ref of the node?
const node = useRef(null);
...
<div ref={node} onLoad={() => {
node.current.parentNode.style.background = 'yellow';
}} />
Unless there's a really good reason to access the div directly, or as a ref, why not just use props?
Edit: I've included an example of how to use useRef to set the parent height using the child's calculated height.
import React, { useState, useEffect, useRef } from "react";
const ParentComponent = () => {
const [height, setHeight] = useState(null)
const node = useRef(null)
return (
<div
style={{
...(height ? { height: `${height}px` } : {}),
backgroundColor: 'yellow',
position: 'relative'
}}
>
<MyComponent setHeight={setHeight} node={node} />
</div>
)
}
const MyComponent = ({ setHeight, node }) => {
useEffect(() => {
const childHeight = node.current ? node.current.offsetHeight : 0
setHeight(childHeight), [node.current]
})
// sample parent updates when child updates
const [content, setContent] = useState(['child'])
useEffect(
() => setTimeout(() => setContent([...content, 'child']), 1000),
[content]
)
return (
<div style={{ position: 'absolute', top: 0, left: 0 }} ref={node}>
{content.map((item, i) => (
<div key={i}>{item + i}</div>
))}
</div>
)
}

ReactJs Functional Component Get When Scroll Reached End

I have a functional component in ReactJs and want to add pagination to my page so that when the user reaches end of the screen, I'd get the rest of the data from Api.
my code looks like this:
<div
className="MyOrdersHistory-list-container">
{ordersList.map((order, index) => (
<div key={index} className="MyOrdersHistory-listItem-container">
........
</div>
))}
</div>
How can I figure out when use scrolls to the end of the page to fire the Api request. (If possible please give a functional component example not a class one)
Thanks
import React, { useRef, useEffect } from 'react';
const <YourComponent> = () => {
const list = useRef();
const onScroll = () => {
if (list.current) {
const { scrollTop, scrollHeight, clientHeight } = list.current;
if (scrollTop + clientHeight === scrollHeight) {
// DO SOMETHING WHAT YOU WANT
}
}
};
...
<div
onScroll={() => onScroll()} ref={list}
className="MyOrdersHistory-list-container">
{ordersList.map((order, index) => (
<div key={index} className="MyOrdersHistory-listItem-container">
........
</div>
))}
</div>

scroll to element on reload page react

I'm trying to figure out how to scroll to the element after page reloads. I'm trying to ref the element and scroll to it after page reloads.
My current example will just scroll to the end of the page
const transactionRef = useRef(null)
const scrollToElemet = () => {
transactionRef.current.scrollIntoView({ block: 'end', behavior: 'smooth' });
}
useEffect(scrollToElemet)
return (
<div ref={transactionRef}></div>
)
How can I calculate the position of the element and scroll to it on page reload? Thanks
Adding this answer because querying the DOM in react is an anti-pattern:
export default function App() {
const footerRef = useRef();
useEffect(() => {
footerRef.current.scrollIntoView();
}, []);
return (
<>
<div className="main">Main</div>
<div ref={footerRef} className="footer">
Footer
</div>
</>
);
}
I would say, add an element at the bottom of your page and then use that element to scroll. Here is a sample
https://codesandbox.io/s/jolly-dhawan-k7q8b?fontsize=14&hidenavigation=1&theme=dark
Also, you need to pass [] as second argument so it only scroll on first mount otherwise it will keep on scrolling down to bottom on every change or on every render.
function App() {
const [checked, setChecked] = useState(false);
useEffect(() => {
document.getElementById("footer").scrollIntoView();
}, []);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<span onClick={() => setChecked(v => !v)}>{checked ? "Enable" : "Disable"}</span>
<h2>Start editing to see some magic happen!</h2>
<div className="main">Main</div>
<div id="footer" className="footer">
Footer
</div>
</div>
);
}

In react, how to get noticed when children change?

I am making this class called Scrollable which enables scrolling if the width/height of the children elements exceeds a certain value. Here is the code.
import React, { Component } from 'react';
const INITIAL = 'initial';
class Scrollable extends Component {
render() {
let outter_styles = {
overflowX: (this.props.x? 'auto': INITIAL),
overflowY: (this.props.y? 'auto': INITIAL),
maxWidth: this.props.width || INITIAL,
maxHeight: this.props.height || INITIAL,
};
return (
<div ref={el => this.outterEl = el} style={outter_styles}>
<div ref={el => this.innerEl = el}>
{this.props.children}
</div>
</div>
);
}
};
export default Scrollable;
// To use: <Scrollable y><OtherComponent /></Scrollable>
This works great. Except now I wish to add one more functionality which makes the scrollable always scroll to the bottom. I have some idea of how to do it:
this.outterEl.scrollTop = this.innerEl.offsetHeight;
But this only need to be called when this.props.children height changes. Is there any idea on how to achieve this?
Thanks in advance.
I would recommend a package element-resize-detector. It is not React-specific but you can easily build a high-order component around it or integrate your Scrollable component with it.
Now I have an idea of solving this.
Since I am using react-redux. The problem is that I could not use lifecycle hooks on this Scrollable component since this.props.children might not necessarily be changed when the content is updated.
One way to achieve this is to make Scroll component aware of the corresponding values in the redux state. So that when that relevant value is updated, we can scroll down to the bottom.
Scrollable component:
import React, { Component } from 'react';
const INITIAL = 'initial';
class Scrollable extends Component {
componentWillUpdate(){
if(this.props.autoScroll){
// only auto scroll when the scroll is already at bottom.
this.autoScroll = this.outterEl.scrollHeight - this.outterEl.scrollTop - Number.parseInt(this.props.height) < 1;
}
}
componentDidUpdate(){
if(this.autoScroll) this.outterEl.scrollTop = this.outterEl.scrollHeight;
}
render() {
let styles = {
overflowX: (this.props.x? 'auto': INITIAL),
overflowY: (this.props.y? 'auto': INITIAL),
maxWidth: this.props.width || INITIAL,
maxHeight: this.props.height || INITIAL,
};
return (
<div ref={el => this.outterEl = el} style={styles}>
<div ref={el => this.innerEl = el}>
{this.props.children}
</div>
</div>
);
}
};
export default Scrollable;
Scrollable container:
import { connect } from 'react-redux';
import Scrollable from '../components/Scrollable';
const mapStateToProps = (state, ownProps) => Object.assign({
state: state[ownProps.autoScroll] || false
}, ownProps);
export default connect(mapStateToProps)(Scrollable)
With this, Scrollable's life cycle hooks will be called when the corresponding state changes.

Categories

Resources