I am trying to build an infinite scroll in a div with a fixed height and a scroll attached to it, so my goal is for the window not to move but a component within to have a scroll and the items within to be added infinatly.
this is what i have so far:
import React, { useState } from "react";
import ReactDOM from "react-dom";
import InfiniteScroll from "react-infinite-scroll-component";
const style = {
height: 18,
border: "1px solid green",
margin: 6,
padding: 8
};
const DoseListCardBody = () => {
const [items, setItems] = useState(Array.from({ length: 20 }));
const fetchMoreData = () => {
setItems(items.concat(Array.from({ length: 10 })));
};
return (
<div style={{ height: "100%", overflowY: "scroll" }}>
<InfiniteScroll
dataLength={items.length}
next={fetchMoreData}
hasMore={items.length < 200}
loader={<h4>Loading...</h4>}
>
{items.map((i, index) => (
<div style={style} key={index}>
div - #{index}
</div>
))}
</InfiniteScroll>
</div>
);
};
ReactDOM.render(
<div style={{ height: "35rem", background: "black" }}>
<div style={{ height: "30rem", background: "white" }}>
<DoseListCardBody />
</div>
</div>,
document.getElementById("root")
);
everything works fine if i change
ReactDOM.render(
<div style={{ height: "35rem", background: "black" }}>
<div style={{ height: "30rem", background: "white" }}>
<DoseListCardBody />
</div>
</div>,
document.getElementById("root")
);
to
ReactDOM.render(
<DoseListCardBody />,
document.getElementById("root")
);
I think this is because it is using the scroll of the window not the component.
How do i get InfiniteScroll to use the parent component or a component with a scroll that I specify.
I appologise for the bad terminology, i dont usualy develop web pages.
ok got it!
one must use scrollableTarget as a prop in the InfiniteScroll and specify the ID of the compnent that has the scrollbar.
example:
const DoseListCardBody = () => {
const [items, setItems] = useState(Array.from({ length: 20 }));
const fetchMoreData = () => {
setItems(items.concat(Array.from({ length: 10 })));
};
return (
<div id="scrollableDiv" style={{ height: "100%", overflowY: "scroll" }}>
<InfiniteScroll
dataLength={items.length}
next={fetchMoreData}
hasMore={items.length < 200}
loader={<h4>Loading...</h4>}
scrollableTarget="scrollableDiv"
>
{items.map((i, index) => (
<div style={style} key={index}>
div - #{index}
</div>
))}
</InfiniteScroll>
</div>
);
};
notice the addition of 'id="scrollableDiv"' and 'scrollableTarget="scrollableDiv"'.
Related
React virtualized WindowScroller is not rendering new rows if we have max-height to parent element. Below is the codesandbox link where the issue is reproduced.
https://codesandbox.io/s/react-virtualized-windowscroller-not-working-for-max-height-ribjod
import React from "react";
import { render } from "react-dom";
import { WindowScroller, List, AutoSizer } from "react-virtualized";
import "react-virtualized/styles.css";
const list = Array.from({ length: 100 }).map(
(_, index) => `list item ${index + 1}`
);
const rowRenderer = ({ index, style }) => (
<div key={index} style={style}>
{list[index]}
</div>
);
const App = () => {
return (
<div style={{ background: "yellow" }}>
<div style={{ background: "red" }}>Some content</div>
<div
class="windowScrollWrapper"
style={{ "maxHeight": "200px", overflow: "auto" }}
>
<WindowScroller className="windowScroller">
{({ height, onChildScroll, scrollTop }) => (
<AutoSizer className="AutoSizer" disableHeight={true}>
{({ width }) => {
return (
<List
autoHeight
height={height}
rowCount={list.length}
rowHeight={32}
rowRenderer={rowRenderer}
onScroll={onChildScroll}
scrollTop={scrollTop}
width={width}
/>
);
}}
</AutoSizer>
)}
</WindowScroller>
</div>
<div style={{}}>Some other content</div>
</div>
);
};
render(<App />, document.getElementById("root"));
I want to synchronize a divs scroll with a body scroll.
I tried some examples with two divs but I couldn't manage fix it with the body scroll.
Sample code with two divs: https://codesandbox.io/s/react-custom-scroll-sync-of-2-divs-10xpi
My Code
https://codesandbox.io/s/funny-rain-ditbv
import "./styles.css";
import { useRef } from "react";
export default function App() {
const firstDivRef = useRef();
const secondDivRef = useRef();
const handleScrollFirst = (scroll) => {
secondDivRef.current.scrollTop = scroll.target.scrollTop;
};
const handleScrollSecond = (scroll) => {
firstDivRef.current.scrollTop = scroll.target.scrollTop;
};
return (
<div
className="App"
style={{
display: "flex",
}}
>
<div
onScroll={handleScrollFirst}
ref={firstDivRef}
style={{
height: "500px",
overflow: "scroll",
backgroundColor: "#FFDAB9",
position: "sticky",
top: "0px"
}}
>
<div style={{ height: 5000, width: 300 }}>
The first div (or it can be tbody of a table and etc.)
{[...new Array(1000)].map((_, index) => {
const isEven = index % 2 === 0;
return (
<div style={{ backgroundColor: isEven ? "#FFFFE0 " : "#FFDAB9" }}>
{index}
</div>
);
})}
</div>
</div>
<div
onScroll={handleScrollSecond}
ref={secondDivRef}
style={{
height: "100%",
backgroundColor: "#EEE8AA"
}}
>
<div style={{ height: 5000, width: 200 }}>
The second div
{[...new Array(1000)].map((_, index) => {
const isEven = index % 2 === 0;
return (
<div style={{ backgroundColor: isEven ? "#FFFFE0 " : "#FFDAB9" }}>
{index}
</div>
);
})}
</div>
</div>
</div>
);
}
It was easy to use different divs rather than using a div and window.
But finally managed to run it with a div and the body.
The trick is they block each other since they listen each others values.
import "./styles.css";
import { useEffect, useRef, useState } from "react";
export default function App() {
const firstDivRef = useRef();
const [scrollTop, setScrollTop] = useState(0);
const [disableBodyScroll, setDisableBodyScroll] = useState(false);
const handleScrollFirst = (scroll) => {
setScrollTop(scroll.target.scrollTop);
};
useEffect(() => {
if (firstDivRef.current && !disableBodyScroll) {
firstDivRef.current.scrollTop = scrollTop;
}
if (disableBodyScroll) {
window.scrollTo(0, scrollTop);
}
}, [firstDivRef, scrollTop, disableBodyScroll]);
useEffect(() => {
const onScroll = () => {
console.log(disableBodyScroll, window.scrollY);
if (!disableBodyScroll) {
setScrollTop(window.scrollY);
}
};
// clean up code
window.removeEventListener("scroll", onScroll);
window.addEventListener("scroll", onScroll);
return () => window.removeEventListener("scroll", onScroll);
}, [disableBodyScroll]);
return (
<div
className="App"
style={{
display: "flex"
}}
>
<div
onMouseEnter={() => setDisableBodyScroll(true)}
onMouseLeave={() => setDisableBodyScroll(false)}
onScroll={handleScrollFirst}
ref={firstDivRef}
style={{
height: "500px",
overflow: "scroll",
backgroundColor: "#FFDAB9",
position: "sticky",
top: "0px"
}}
>
<div style={{ height: 5000, width: 300 }}>
The first div (or it can be tbody of a table and etc.)
{[...new Array(1000)].map((_, index) => {
const isEven = index % 2 === 0;
return (
<div style={{ backgroundColor: isEven ? "#FFFFE0 " : "#FFDAB9" }}>
{index}
</div>
);
})}
</div>
</div>
<div
style={{
height: "100%",
backgroundColor: "#EEE8AA"
}}
>
<div style={{ height: 5000, width: 200 }}>
The second div
{[...new Array(1000)].map((_, index) => {
const isEven = index % 2 === 0;
return (
<div style={{ backgroundColor: isEven ? "#FFFFE0 " : "#FFDAB9" }}>
{index}
</div>
);
})}
</div>
</div>
</div>
);
}
https://codesandbox.io/s/ancient-dream-tzuel?file=/src/App.js
Try the next example. This is a quick sketch but maybe it will help you.
https://codesandbox.io/s/gallant-goldwasser-19g4d?file=/src/App.js
I am trying to synchronize scrolling in my React page. As you can see, I have a div-container containing an arbitrary number of one Component, depending on the length of the array. I've tried react-scroll-sync and scroll-sync-react, but to no avail... Any ideas?
Edit: to be more precise, there is an iframe in each component that is the scrollable part.
<div>
{array.map((value, index) => (
<MyComponent style={{ overflow: 'auto' }} />
))}
</div>
I tried the following: Removing the Component and just producing arr.length-times iframes. But that also didn't work:
import "./styles.css";
// import { ScrollSync, ScrollSyncNode } from "scroll-sync-react";
import { ScrollSync, ScrollSyncPane } from "react-scroll-sync";
const arr = [1, 2, 3];
export default function App() {
return (
<ScrollSync>
<div
className="App"
style={{ display: "flex", height: "500px", width: "500px" }}
>
{arr.map((element, index) => (
<ScrollSyncPane key={arr[index]}>
<iframe
src={mySource}
key={arr[index]}
title={arr[index]}
styles={{ height: "300px", width: "200px", overflow: "auto" }}
/>
</ScrollSyncPane>
))}
</div>
</ScrollSync>
);
}
I have 2 sibling components in 1 parent component. It's like this:
import React, { useState } from 'react';
import PlaceSearchInput from './PlaceSearchInput';
import { GoogleMap, withGoogleMap } from "react-google-maps";
export default function Search(props) {
const [mapCenter, setMapCenter] = useState({lat:3, lng:2});
function Map() {
return (
<div>
<GoogleMap defaultZoom={10} center={mapCenter}/>
</div>
)
}
const WrappedMap = withGoogleMap(Map);
if (!props.isGoogleMapApiReady)
return (
<div>Loading...</div>
)
return (
<div style={{ margin: "100px" }}>
<div style={{ height: "50vh", width: "50vh" }}>
<WrappedMap
loadingElement={<div style={{ height: "100%" }} />}
containerElement={<div id="map" style={{ height: "100%" }} />}
mapElement={<div style={{ height: "100%" }} />}
/>
<PlaceSearchInput setMapCenter={setMapCenter} />
</div>
</div>
)
}
I want Input sets coordinates and Map shows the coordinates. I know one way is that having coordinates state in parent and then passing set function to Input, coordinates to Map. But with this way I found out it whenever state is changed by Input component, though Map does move to new coordinates, Map is refreshed and that is what I want to avoid. Is there way to solve it?
Try this, I moved Map and WrappedMap's creation out of the of the Search component.
I believe that the change in the component definition every time the component re-rendered likely caused react to think it's an entirely new component and unmount the old and mount the new rather than re-render.
import React, { useState } from 'react';
import PlaceSearchInput from './PlaceSearchInput';
import { GoogleMap, withGoogleMap } from 'react-google-maps';
function Map({ center }) {
return (
<div>
<GoogleMap defaultZoom={10} center={center} />
</div>
);
}
const WrappedMap = withGoogleMap(Map);
export default function Search(props) {
const [mapCenter, setMapCenter] = useState({ lat: 3, lng: 2 });
if (!props.isGoogleMapApiReady) {
return <div>Loading...</div>;
}
return (
<div style={{ margin: '100px' }}>
<div style={{ height: '50vh', width: '50vh' }}>
<WrappedMap
loadingElement={<div style={{ height: '100%' }} />}
containerElement={<div id="map" style={{ height: '100%' }} />}
mapElement={<div style={{ height: '100%' }} />}
center={mapCenter}
/>
<PlaceSearchInput setMapCenter={setMapCenter} />
</div>
</div>
);
}
I checked the documentation about Lazy Loading components on the official next js docs page (https://nextjs.org/learn/excel/lazy-loading-components).
I tried the steps mentioned and it did not work for me. Below is the piece of code that I want to lazy-load:
<div id="cards" className={index.sectionCards} style={{paddingBottom: '0px'}}>
<div className="title" style={{marginBottom: "0px", paddingBottom: "0px"}}>
Take a Look at Our Exciting Range of Cards
</div>
{this.renderCards()}
<div></div>
</div>
Here the renderCards function makes a call to a backend API and gets images from AWS S3, this whole process takes a lot of time and hence increases the overall page load time, below is the code for the function renderCards:
renderCards() {
const keys = Object.keys(this.state.products);
const valid_keys = ['Specials', 'New Beginnings', 'Expressions', 'Celebrations' ];
if(keys.length == 0) return <div></div>
return (<div className={index.cards}>
{
keys.map((key) => {
if(valid_keys.indexOf(key) > -1) return <div style={{ width: '80%', margin: '0 auto' }}>
<div className={index.category}>{key}</div>
<div style={{ display: 'flex', overflow: 'scroll' }} >
{this.state.products[key].map((c) => {
if(c.status == 'PRODUCT_ACTIVE') {
return <img onClick={() => this.onClickProduct(c)} className={index.cardImage} src={`<backend URL here>`} />
}
})}
</div>
</div>
})
}
</div>)
}
The objective was to lazy load this component to improve the overall page speed.
If anyone knows a way to solve this problem, please share.
Documentation for dynamic/lazy loading with nextjs
const Cards = () => {
const renderCards = () => {
const keys = Object.keys(this.state.products);
const valid_keys = [
"Specials",
"New Beginnings",
"Expressions",
"Celebrations",
];
if (keys.length == 0) return <div></div>;
return (
<div className={index.cards}>
{keys.map((key) => (
<Fragment key={key}>
{valid_keys.indexOf(key) > -1 && (
<div style={{ width: "80%", margin: "0 auto" }}>
<div className={index.category}>{key}</div>
<div style={{ display: "flex", overflow: "scroll" }}>
{this.state.products[key].map((c) => (
<Fragment key={c.id}>
{c.status === "PRODUCT_ACTIVE" && (
<img
onClick={() => this.onClickProduct(c)}
className={index.cardImage}
src={`<backend URL here>`}
/>
)}
</Fragment>
))}
</div>
</div>
)}
</Fragment>
))}
</div>
);
};
return (
<div
id="cards"
className={index.sectionCards}
style={{ paddingBottom: "0px" }}
>
<div
className="title"
style={{
marginBottom: "0px",
paddingBottom: "0px",
}}
>
Take a Look at Our Exciting Range of Cards
<div>{this.renderCards()}</div>
</div>
</div>
);
};
```
above code is not my code but a replication of the code in the question.
```
```
from nextjs
For the best understanding of dynamic/lazy load see link provided.
import dynamic from "next/dynamic";
```
import LoadSpinner from "../loadSpinner";
const Cards = dynamic(() => import("./cards"), {
```
this can be a custom loader or a node_module installed or just <div>Loading...</div> the loading: function will display while waiting for the import fucntion to load.
loading: () => <LoadSpinner />,
```
});
const CardContainer = () => ( <Cards /> );