Synchronize Scrolling over mapped React Components - reactjs

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>
);
}

Related

How to access values from context in a separate functional component

I'm trying to build a simple light mode/dark mode into my app I saw this example on Material UI for light/dark mode but I'm not sure how I can get access to the value for when the user clicks toggleColorMode in my Header component if it's being set in toggleColorMode function?
I guess my question is how can I get access to the value of light/dark mode of the context in my Header component if it's in a different function?
Here is my code.
import React, { useState, useEffect } from "react";
import MoreVertIcon from "#mui/icons-material/MoreVert";
import DarkModeIcon from "#mui/icons-material/DarkMode";
import LightModeIcon from "#mui/icons-material/LightMode";
import Paper from "#mui/material/Paper";
import { useTheme, ThemeProvider, createTheme } from "#mui/material/styles";
import IconButton from "#mui/material/IconButton";
import Navigation from "../Navigation/Navigation";
const ColorModeContext = React.createContext({ toggleColorMode: () => {} });
export const Header = (props) => {
const { mode } = props;
const theme = useTheme();
const colorMode = React.useContext(ColorModeContext);
console.log("mode is...", mode);
return (
<div className="header-container">
<Paper
elevation={3}
style={{ backgroundColor: "#1F1F1F", padding: "15px" }}
>
<div
className="header-contents"
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
}}
>
<div
className="logo"
style={{ display: "flex", alignItems: "center" }}
>
<img
src="/images/header-logo.png"
alt="URL Logo Shortener"
width={"50px"}
/>
<h1 style={{ color: "#ea80fc", paddingLeft: "20px" }}>
URL Shortener
</h1>
</div>
<div className="settings">
<IconButton
sx={{ ml: 1 }}
onClick={colorMode.toggleColorMode}
color="inherit"
aria-label="dark/light mode"
>
{theme.palette.mode === "dark" ? (
<DarkModeIcon
style={{
cursor: "pointer",
marginRight: "10px",
}}
/>
) : (
<LightModeIcon
style={{
cursor: "pointer",
marginRight: "10px",
}}
/>
)}
</IconButton>
<IconButton aria-label="settings">
<MoreVertIcon style={{ color: "#fff", cursor: "pointer" }} />
</IconButton>
</div>
</div>
</Paper>
{/* Navigation */}
<Navigation />
</div>
);
};
export default function ToggleColorMode() {
const [mode, setMode] = React.useState("light");
const colorMode = React.useMemo(
() => ({
toggleColorMode: () => {
setMode((prevMode) => (prevMode === "light" ? "dark" : "light"));
},
}),
[]
);
const theme = React.useMemo(
() =>
createTheme({
palette: {
mode,
},
}),
[mode]
);
return (
<ColorModeContext.Provider value={colorMode}>
<ThemeProvider theme={theme}>
<Header mode={mode} />
</ThemeProvider>
</ColorModeContext.Provider>
);
}
Read the documentation: createContext, useContext. You need to render a ContextProvider in your parent (or top-level) component, then you can get the data in any component in the tree like const { theme } = useContext(ColorModeContext);.
You don't need to pass the mode as props, put it as one of the values in the context and access it.
Here's how you would render it in your example:
<ColorModeContext.Provider value={{colorMode, theme}}>
<Header />
</ColorModeContext.Provider>
You can pass an object inside the value in the context provider, in other word you can pass the toggle function inside your value to be consumed in the childern. thus you gain an access to change your mode state.
Note that the way changes are determined can cause some issues when passing objects as value, this might trigger unnecessary rerendering see Caveats for more info. or refer to the useContext docs
<ColorModeContext.Provider
value={{ colorMode: colorMode, toggleColorMode: toggleColorMode }}
>
<ThemeProvider theme={theme}>
<Header />
</ThemeProvider>
</ColorModeContext.Provider>

how can pass the src of an in an array of object through a props to another components,Can someone explain to me why I can't show my Images

import React from 'react';
import Gallery from '../Gallery/Gallery';
const Galleries = (props) => {
const galleries = [
{
name: 'natur',
src: ' ../../Images/photographer-gfc1c015b1_1920.jpg',
},
{
name: 'moutain',
src: ' ../../Images/photographer-gfc1c015b1_1920.jpg',
},
];
return (
<div>
{/* generate the numbre of data */}
<h1>I have {galleries.length} Images</h1>
{galleries.map((gallery) => (
<Gallery name={gallery.name} galery={gallery.src} />
))}
</div>
);
};
export default Galleries;
import React from 'react';
const Gallery = (props) => {
return (
<div>
<h1>{props.name}</h1>
<img src={props.src} alt="" style={{ height: '200px', width: '300px' }} />
</div>
);
};
export default Gallery;
here is your mistake, wrong props are getting used,
you are passing this <Gallery name={gallery.name} galery={gallery.src} />
change this line, this is wrong line, using src, but you never passed src,
<img src={props.src} alt="" style={{ height: '200px', width: '300px' }} />
change to this, this will work fine, props.galery works
<img src={props.galery} alt="" style={{ height: '200px', width: '300px' }} />

how to have set state for sibling component in React without updating

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>
);
}

React InfiniteScroll in a scrollable component on the page

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"'.

What is best way to do lazy loading in a Next js Application?

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 /> );

Resources