I want to store previous offset of infinite list.my way is storing offset when user scrolling into sessionStorage, then use it whenever user back to this component.But when i navigate to another component then back to here, window.scrollBy() not working.Am i missing something or wrong somewhere of my code?
all my code
const Home = () => {
...
const navigateToChat = useNavigate();
const handleNavigateToChat = () => {
navigateToChat("/chat");
};
//Check if user scrolled then get this value to for window.scrollBy()
useEffect(()=>{
const handleGetOffset = () => {
const scrolledIndex = sessionStorage.getItem("scrolledIndex")
scrolledIndex && window.scrollBy(0,scrolledIndex)
console.log(scrolledIndex)
};
handleGetOffset ()
},[])
//Get yOffset then storing into sessionStorage
useEffect(() => {
const handleScroll = () => {
sessionStorage.setItem("scrolledIndex",window.scrollY)
};
window.addEventListener("scroll", handleScroll);
return () =>{
window.removeEventListener("scroll", handleScroll)
}
},[]);
const handleFindingUser = async () => {
//Get data from server
...
};
...
return (
<InfiniteScroll
dataLength={users.length} //This is important field to render the next data
next={handleFindingUser}
hasMore={hasMore}
loader={<h4>Loading...</h4>}
endMessage={
<p style={{ textAlign: "center" }}>
<b>Yay! You have seen it all</b>
</p>
}
>
{users.map((user) => {
return (
<div
key={user.userId}
id={user.nickName}
className="d-flex"
style={{ margin: 12 }}
>
<p>{user.nickName}</p>
<Button
onClick={handleNavigateToChat}
variant="info"
style={{ marginLeft: 20 }}
>
Chat Now
</Button>
</div>
);
})}
</InfiniteScroll>
);
};
export default Home;
have a nice day, everyone!
Related
I have a div(card) as below. When I click on this card, a detail page opens and when I come back to this page, I want the jackson text to be grayed out. Makes handleClick detail page open.
like this
export default function ListComponent({ handleClick }) {
const [data, setData] = useState([]);
const [isSelected, setIsSelected] = useState(false);
useEffect(() => {
getMailbox()
.then((response) => {
if (response?.success) {
setData(response?.data?.mails);
}
})
.catch((err) => {
console.log("err", err);
});
}, []);
return (
<div className={styles.messageBoxContainer}>
{data.map((item, index) => (
<div
key={`item-container-${index}`}
className={styles.mailItemContainer}
onClick={() => {
handleClick(item);
}}>
<div className={styles.mailOwner}>
<p className={isSelected ? styles.userEmailTextSelected : styles.userEmailText}>{item?.senderName}</p>
</div>
</div>
))}
</div>
);
}
You can add a return field to your useEffect hook so that when the element is unmounted its color change.
useEffect(() => {
getMailbox()
//...
return () => {
document.querySelector('#yourElementID').style.color = 'gray'
}
}, []);
I have such a project. Here I want the button border save in the local storage.The buttons are divided into categories. For example when you refresh the page after selecting a sports button, the border of the button disappears. I want save btn border in the localstorage. I saved the categories in memory, but I can't make the border of the selected button.How can I fix it?
import React, { useEffect, useState } from "react";
import SpinnerLoad from './components/SpinnerLoad'
import NewsItem from "./components/NewsItem";
import Category from "./components/data/Category"
const App = () => {
const [state, setState] = useState([]);
const [loading, setLoading] = useState(false)
const [selected, setSelected] = useState('');
const fetchValue = (category, index) => {
localStorage.setItem("category", category);
localStorage.setItem("selected", index);
fetch(`https://inshorts-api.herokuapp.com/news?category=${category}`)
.then(res => res.json())
.then(res => {
setState(res.data)
setLoading(true)
})
.catch((error) => console.log(error))
setLoading(false);
};
const CategoryButton = ({ category, i }) => (
// passing index --> i to the fetch Value
<button onClick={() =>{ fetchValue(category,i) ; setSelected(i)} }
style={{border : selected === i ? '1px solid red' : null}} >{category}</button>
);
useEffect(() => {
let categoryValue = localStorage.getItem("category") || "all";
fetchValue(categoryValue)
const select = localStorage.getItem("selected") || "";
setSelected(select);
}, []);
return (
<>
<div className="header-bg">
<h1 className="mb-3">News</h1>
<div className="btns ">
{Category.map((value,i) => {
return <CategoryButton category={value} i={i}/>;
})}
</div>
</div>
<div className="news">
<div className="container">
<div className="row">
{
!loading
? <SpinnerLoad />
:
state.map((data, index) => {
return (
<NewsItem
imageUrl={data.imageUrl}
author={data.author}
title={data.title}
content={data.content}
date={data.date}
key={data.id}
/>
);
})
}
</div>
</div>
</div>
</>
);
};
export default App;
According to the code looks like you want to display data specific to a category set when the user clicks on the category buttons. and after the click, the correct data is rendered and the current category button receives a change in its style highlighting it is the current state.
I don't understand why you need to store anything in a client's localstorage,
I would not recommend storing too much in localStorage as it is limited and is used by different sites a user visits, I only store authentication tokens in localstorage and I believe that is the norm.
I've tried to create the effect you want without the need to store in local storage
import React, { useState, useCallback, useEffect } from "react";
import ReactDOM from "react-dom";
import { cat } from "../categories.js";
import { news } from "../news.js";
function Example() {
const [state, setState] = useState([]);
const [loading, setLoading] = useState(false);
const [selected, setSelected] = useState(null);
useEffect(() => {
function fetchFunction() {
setLoading(true);
for (let i = 0; i < news.length; i++) {
if (news[i].id === selected) {
const current = news[i].c;
setState(current);
}
}
setLoading(false);
}
fetchFunction();
}, [selected]);
return (
<>
<ol
style={{
width: "50%",
listStyle: "none",
display: "flex",
justifyContent: "space-between"
}}
>
{cat.map((item, index) => {
return (
<li key={index}>
<button
style={{ border: selected === item.id && "none" }}
onClick={() => {
setSelected(item.id);
}}
>
{item.name}
</button>
</li>
);
})}
</ol>
<section style={{ width: "100%", height: "70%" }}>
{state.map((item, index) => {
return (
<div
key={index}
style={{
width: "30%",
height: "30%",
background: "red",
display: "flex",
alignItems: "center",
justifyContent: "center",
margin: "1% 0 2% 0"
}}
>
{item.name}
</div>
);
})}
</section>
</>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<Example />, rootElement);
You can save the selectedIndex in localStorage and retrieve it in the useEffect..
const CategoryButton = ({ category, i }) => (
// passing index --> i to the fetch Value
// setting selected as string instead of index for type checking
<button onClick={() =>{ fetchValue(category,i) ; setSelected(`${i}`)} }
style={{border : selected === `${i}` ? '1px solid red' : null}} >{category}</button>
);
const fetchValue = (category, index) => {
localStorage.setItem("category", category);
localStorage.setItem("selected", index);
// ...
}
useEffect(() => {
const select = localStorage.getItem("selected") || "";
// passing selectedIndex to the fetchValue, otherwise it becomes
//undefined..
fetchValue(categoryValue,select)
setSelected(select);
},[])
IMessage is an interface with type string. The app is something like a todo list, but I need it to identify when a URL is typed in and convert it to a clickable link
const [message, setMessage] = useState<string>("");
const [chat, setChat] = useState<IMessage[]>([]);
const regex = /https?:\/\/(www\.)?[-a-zA-Z0-9#:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()#:%_\+.~#?&//=]*)/;
useEffect(() => {
chat.forEach(chat => {
///function
})
})
here's a bigger piece of code that I have at the moment
const Home: NextPage = () => {
const [message, setMessage] = useState<string>("");
const [chat, setChat] = useState<IMessage[]>([]);
const regex = /https?:\/\/(www\.)?[-a-zA-Z0-9#:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()#:%_\+.~#?&//=]*)/;
const sendMessage = () => {
const newMessage = { message: message };
setChat([...chat, newMessage]);
setMessage("");
};
const inputChanged = (
event: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
) => setMessage(event.target.value);
return (
<div className={styles.container}>
<Stack
direction="column"
spacing={2}
justifyContent="center"
alignItems="flex-end"
>
{chat.map((message: IMessage) => {
return (
<Paper
style={{
backgroundColor: "#6096ba",
padding: "5px",
borderRadius: "30px",
}}
elevation={3}
>
<p style={{ maxWidth: "20ch", color: "white" }}>
{message.message}
</p>
</Paper>
);
})}
</Stack>
```
Here you can use it.includes() if I got you right, Then you add whatever you want to do with it.
-Another tip: Create your function out of useEffect then just call it inside the effect, you might have a big project then you will find it harder and not useful at all
You don't need to use useEffect for this if you want it to change the rendering to be a link.
Your return would look something like the following. You would choose to either render as plain text or as a link.
return (
<ol>
{chat.map((chatMessage) => {
return chatMessage.match(regex) ? (
<li>
<a href={chatMessage}>{chatMessage}</a>
</li>
) : (
<li>{chatMessage}</li>
);
})}
</ol>
);
I want to make a like button where user can click and like something. When user clicks the button remains red even after refresh. How can i implement this?
I have this code. When i refresh the local storage gets reset. How can i get around this?
useEffect(() => {
setColor(window.localStorage.getItem('color'));
}, []);
useEffect(() => {
window.localStorage.setItem('color', color);
}, [color]);
const handleClick = () => {
setClicked(prevValue => !prevValue)
if(clicked){
setColor("red")
}else{
setColor("")
}
}
<div className="App">
<div className="container">
<button style={{backgroundColor: color}} onClick={handleClick} > +</button>
</div>
</div>
Try this approach. We need check twice localStorage first when the component mounting, second when we clicked the button. example
App.js
import { useState, useEffect } from "react";
const App = () => {
const [color, setColor] = useState("");
useEffect(() => {
const lS = window.localStorage.getItem("color");
if (lS) return setColor(lS);
localStorage.setItem("color", "");
}, []);
const handleClick = () => {
const lS = window.localStorage.getItem("color");
if (lS === "") {
localStorage.setItem("color", "red");
setColor("red");
}
if (lS !== "") {
localStorage.setItem("color", "");
setColor("");
}
};
return (
<div className="App">
<div className="container">
<button
style={{ backgroundColor: color }}
className="like-button"
onClick={handleClick}
>
+
</button>
</div>
</div>
);
};
export default App;
I have tried to duplicate this error in a sandbox. However, on my machine it works. Could it be that you have localStorage.removeItem('color') somewhere else in your project and gets called? Or maybe a problem with your browser. Here is the sandbox where it works: https://codesandbox.io/s/magical-shannon-cot7i?file=/src/App.js
I hope, it will work I have not tested it but I am sure it should work
useEffect(() => {
const storedColor = localStorage.getItem('color')
if(storedColor) {
setColor(storedColor);
}
}, []);
const handleClick = () => {
setClicked(prevValue => !prevValue)
if(clicked){
setColor("red");
localStorage.setItem('color', color);
}else{
setColor("")
}
}
return <div className="App">
<div className="container">
<button style={{backgroundColor: color}} onClick={handleClick} > + </button>
</div>
</div>
I have 2 onClick functions
function VisitGallery(name) {
const history = useHistory();
console.log("visitgallery", name)
history.push("/gallery")
}
function App() {
const accesstoken = "******************"
const [viewport, setviewport] = React.useState({
latitude: ******
longitude: *******
width: "100vw",
height: "100vh",
zoom: 11
})
const [details, setdetails] = React.useState([
])
React.useEffect(() => {
const fetchData = async () => {
const db = firebase.firestore()
const data = await db.collection("data").get()
setdetails(data.docs.map(doc => doc.data()))
}
fetchData();
}, [])
const [selectedpark, useselectedpark] = React.useState(null);
React.useEffect(() => {
const listener = e => {
if (e.key === "Escape") {
useselectedpark(null);
}
};
window.addEventListener("keydown", listener)
return () => {
window.removeEventListener("keydown", listener)
}
}, [])
return (
<div className="App">
<ReactMapGl {...viewport}
mapboxApiAccessToken={accesstoken}
mapStyle="mapbox://**************"
onViewportChange={viewport => {
setviewport(viewport)
}}>
{details.map((details) =>
<Marker key={details.name} latitude={details.lat} longitude={details.long}>
<button class="marker-btn" onClick={(e) => {
e.preventDefault();
useselectedpark(details);
}}>
<img src={icon} alt="icon" className="navbar-brand" />
</button>
</Marker>
)}
{selectedpark ?
(<Popup
latitude={selectedpark.lat}
longitude={selectedpark.long}
onClose={() => {
useselectedpark(null);
}}
>
<div>
<Card style={{ width: '18rem' }}>
<Card.Body>
<Card.Title>{selectedpark.name}</Card.Title>
<Card.Text>
{selectedpark.postalcode}
</Card.Text>
<Button variant="primary" onClick = VisitGallery() >Visit Gallery</Button>
</Card.Body>
</Card>
</div>
</Popup>)
: null}
{
console.log("in render", details)
}
</ReactMapGl>
</div>
);
}
export default App;
The outer onClick is assigned when the marker is first created, and when it is clicked the useselectedpark function is called, details is then assigned to selectedpark.
The inner onClick is assigned to the function VisitGallery(). When the inner onClick is triggered, i want to navigate to another page, hence the history.push().
Ideally, what i want for it to happen is, when the outer onClick is triggered, the cardview shows, and i have an option to visit the next page, which can be triggered by an onClick within the card. However, what is happening right now is both the onClicks are triggered when i click on the thumbnail. How do i fix it such that it is how i want it to be ideally?
ps: do let me know if my explanation is confusing and i will edit it accordingly
Try adding your second onClick into a callback function?
<Button variant="primary" onClick='()=>{ VisitGallery() }' >Visit Gallery</Button>
So that it doesn't automatically invoke the function until the click is triggered.