I have a div whose height can sometimes overflow the page's height because of a long content. How to know if it is the case? I tried to compare scrollHeight to clientHeight, but both values are the same. Here is the code:
const Component = ({text}) => {
const ref = React.useRef(null);
const contentOverflowPage = ref.current && ref.current.scrollHeight > ref.current.clientHeight
return (
<Root ref={ref}>
{content}
</Root>
);
};
export default Component;
How to fix this? Thanks!
Related
I have a parent FlatList which, in its footer, can render another flatlist or a simple scroll view.
const renderFooter1 = () => {
return <ScrollView> ... </ScrollView>;
}
const renderFooter2 = () => {
return <FlatList ... />;
}
// Note: I am not doing this in real life, just an example for handling both possible footers
return (
<FlatList
refreshControl={renderRefreshControl()}
ListHeaderComponent={renderHeader()}
ListFooterComponent={Math.random() * 10 > 0.5 ? renderFooter1() : renderFooter2()}
showsVerticalScrollIndicator={false}
/>
);
The problem I am experiencing is that, sometimes, when scrolling down, the component which scrolls is the footer, and not the parent list.
How can I avoid that behavior?
You can add the prop scrollEnabled={false} to whichever FlatList you don't want to scroll.
I've seen tutorials that demonstrates how to do this but all that I've seen uses "window" or examples where the element comes in view from the bottom of a page. I have a modal with a fixed height that scrolls:
const ItemMarkup = forwardRef(({user}, ref) => <li ref={ref}>...</li>)
// This opens up in a modal: https://headlessui.dev/react/dialog
<div className="content">
{/** Tailwind's fixed height 'h-96' */}
<ul ref={scrollRef} className="h-96 overflow-scroll">
{users.map((user, index) =>
<ItemMarkup
{/** I'm looking for the last item/element to come in this view (ul) */}
ref={users.length === index + 1 ? ref : null}
key={user.id}
user={user}
/>
)}
</ul>
</div>
I've looked at this demo but again, it's for the entire page/window. Then I've tried using this function:
// element would be the ref.current
// target would be scrollRef
// But this always return false
function isInViewport(element, target) {
const rect = element.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= target.innerHeight &&
rect.right <= target.innerWidth
);
}
Here's how I tried the attempt:
const ref = useRef()
const scrollRef = useRef()
// From the demo link above
const onScreen = useOnScreen(ref)
// No idea if I need this
const [scrolling, setScrolling] = useState(false)
useEffect(() => {
const onScroll = e => {
setScrolling(true)
console.log(isInViewport(ref.current, scrollRef))
};
scrollRef?.current.addEventListener("scroll", onScroll);
//console.log(ref, onScreen)
return () => scrollRef?.current.removeEventListener("scroll", onScroll);
}, [ref, onScreen, scrolling])
This seems all wrong. Is there a react hook I could to determine when a specific element comes in a modal view or any scrollable view that I specify and not the document view? I just cannot find any documentation/tutorials on this.
I'm creating an infinite scroll. When the last items comes in view, of the modal, I then trigger a function to fetch more data.
I guess I have over complicated things:
useEffect(() => {
const onScroll = e => {
console.log(isInViewport(e.target))
};
scrollRef?.current.addEventListener("scroll", onScroll);
return () => scrollRef?.current.removeEventListener("scroll", onScroll);
}, [])
function isInViewport(element) {
return element.scrollHeight - element.scrollTop === element.clientHeight
}
Now when I scroll to the bottom, I see a console log of true.
same with the title, I want to ask in React how to check overflow text before mounted?
I made an example like this
const OverflowText = (props) => {
const [isOverflow, setIsOverflow] = React.useState(false);
const divRef = React.useRef(null);
useEffect(() => {
if (divRef.current) {
const { scrollHeight, clientHeight, scrollWidth, clientWidth } = divRef.current;
if (scrollHeight > clientHeight || scrollWidth > clientWidth) {
setIsOverflow(true);
} else {
setIsOverflow(false);
}
}
}, [])
const core = <div className={'core'} ref={divRef}>{props.content}</div>
return isOverflow ? (<div className={'wrapper'}>{core}</div>) : (core)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
So you can see in this example, i will check if the text content is overflow by checking scrollHeight, if it is overflow, i will trigger rerender and wrapped it with a div with className wrapper.
Now the problem is with this code, the component will need to render twice, which involve repaint and reflow of the page. Imagine if we got 100 components in a page, this would harm the performance of the page. Therefore, my question is is there anyway I can check if the text is overflow on created (before it even paint) so that it will automatically wrapped with wrapper div in the first time.
Thanks
I have a simple horizontal scroll div.
function Horizontal(){
const [now, setNow] = React.useState(0);
function handleScroll(e){
const { scrollLeft } = e.currentTarget;
setNow(scrollLeft);
}
return (
<>
<p>{now}px</p>
<div width={3000} height={80} onScroll={handleScroll}>
{...}
</div>
</>
)
}
This is an example code and it works well.
but I realize scrollLeft causes reflow.
so, I want to optimize it as well as possible.
Please leave advice if you have any.
I came across this line of code via a snippet on https://usehooks.com,
document.querySelector('body').current
I haven't been able to find .current in the specification at all.
I was hoping someone could clarify its purpose in this context.
It's being used within the IntersectionObserver API in the full example (below) - perhaps the API is exposing the property?
Any help is much appreciated. Thanks in advance.
Following is the full source code:
import { useState, useEffect, useRef } from 'react';
// Usage
function App() {
// Ref for the element that we want to detect whether on screen
const ref = useRef();
// Call the hook passing in ref and root margin
// In this case it would only be considered onScreen if more ...
// ... than 300px of element is visible.
const onScreen = useOnScreen(ref, '-300px');
return (
<div>
<div style={{ height: '100vh' }}>
<h1>Scroll down to next section 👇</h1>
</div>
<div
ref={ref}
style={{
height: '100vh',
backgroundColor: onScreen ? '#23cebd' : '#efefef'
}}
>
{onScreen ? (
<div>
<h1>Hey I'm on the screen</h1>
<img src="https://i.giphy.com/media/ASd0Ukj0y3qMM/giphy.gif" />
</div>
) : (
<h1>Scroll down 300px from the top of this section 👇</h1>
)}
</div>
</div>
);
}
// Hook
function useOnScreen(ref, margin = '0px') {
// State and setter for storing whether element is visible
const [isIntersecting, setIntersecting] = useState(false);
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
// Update our state when observer callback fires
setIntersecting(entry.isIntersecting);
},
{
rootMargin: margin,
root: document.querySelector('body').current
}
);
if (ref.current) {
observer.observe(ref.current);
}
return () => {
observer.unobserve(ref.current);
};
}, []); // Empty array ensures that effect is only run on mount and unmount
return isIntersecting;
}
document.querySelector('body').current is just a property of the body element, which has nothing to do with document.querySelector. It may have been set somewhere else as it is not an existing property of the body element.
var body = document.querySelector("body");
console.log("body.current:", "body.current");
body.current = "SOMEVALUE";
console.log("After setting body.current");
console.log("body.current:", "body.current");
Sorry to disappoint, but it doesn't do anything. It's just a way to supply undefined to the IntersectionObserver API. If you replace document.querySelector('body').current with undefined or remove the entire root field altogether, you still get the same result.
I removed that field to test it to verify the same behavior. Try it yourself in the Codesandbox link here.
As seen by this comment on the example, it can be removed entirely:
You can remove root entirely, since it defaults to the viewport anyway (also document.querySelector('body').current is always undefined, could be document.body but isn't needed anyway)