I have a CryptoCard component from "import {CryptoCard} from "react-ui-flex-cards""
It's placed in a StockCard component which uses material-ui too. And as I can see Material-ui' card component somehow overrides my component.
In HTML inspection I found this:
<div class="card crypto-card"> //this is my component
So as I see it CryptoCard component uses also the card class as css.
If in inspection I turn off background-color: #fafaf" it shows the CryptoCard component with the correct backgroundColor.
Here is my StockCard component
import Axios from 'axios';
import React,{useState, useEffect} from 'react';
import { makeStyles } from '#material-ui/core/styles';
import {CryptoCard} from "react-ui-flex-cards";
function StockCard(props) {
const [FetchInterval, setFetchInterval] = useState(300000);
const [StockData, setStockData] = useState({});
const [TrendDirection, setTrendDirection] = useState(0);
const [Trend, setTrend] = useState(0);
const classes = useStyles();
const FetchData = async () =>{
const resp = await Axios.get(`http://localhost:8080/stock/getquote/${props.API}`)
setStockData(resp.data);
}
const calculateTrendDirection = () => {
if(StockData.currentPrice > StockData.previousClosePrice){
setTrendDirection(1);
} else if (StockData.currentPrice < StockData.previousClosePrice){
setTrendDirection(-1);
} else {
setTrendDirection(0);
}
}
const calculateTrend = () => {
var result = 100 * Math.abs( ( StockData.previousClosePrice - StockData.currentPrice ) / ( (StockData.previousClosePrice + StockData.currentPrice)/2 ) );
setTrend(result.toFixed(2));
}
useEffect(() => {
FetchData();
const interval = setInterval(async() => {
await FetchData();
}, FetchInterval)
return() => clearInterval(interval);
},[FetchInterval]);
useEffect(()=>{
if(StockData.stock){
console.log("Trends calculated", StockData.name);
calculateTrend();
calculateTrendDirection();
}
},[StockData])
return(
<div>
<CryptoCard
currencyName={StockData.stock? StockData.stock.name : "Name"}
currencyPrice={StockData.stock? `$ ${StockData.currentPrice}` : 0}
icon={<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/4/46/Bitcoin.svg/2000px-Bitcoin.svg.png"/>}
currencyShortName={StockData.stock? StockData.stock.symbol : "Symbol"}
trend={StockData.stock? `${Trend} %` : 0}
trendDirection={StockData.stock? TrendDirection : 0}
chartData={[9200, 5720, 8100, 6734, 7054, 7832, 6421, 7383, 8697, 8850]}
/>
</div>
)
}
export default StockCard;
Related
I Can't render my events. Its showing this error -
"Cannot update a component (App) while rendering a different component (EventList). To locate the bad setState() call inside EventList, follow the stack trace as described in https://reactjs.org/link/setstate-in-render"
Here is EventList Component code -
import { useEffect, useState } from "react";
import EventList from "../../event-list";
import EventForm from "../event-form";
const EventAction = ({
getEventsByClockID,
addEvent,
updateEvent,
clockID,
deleteEvent,
deleteEventsByClockID,
}) => {
const [isCreate, setIsCreate] = useState(false);
const [isToggle, setIsToggle] = useState(false);
const [eventState, setEventState] = useState(null)
const handleCreate = () => {
setIsCreate(!isCreate);
}
useEffect(() => {
setEventState(getEventsByClockID(clockID, true));
}, [isToggle])
const handleToggle = () => {
setIsToggle(!isToggle);
}
return (
<div>
<div>
<button onClick={handleCreate}>Create Event</button>
<button onClick={handleToggle}>Toggle Events</button>
</div>
{isCreate && (
<>
<h3>Create Event</h3>
<EventForm
clockID={clockID}
handleEvent={addEvent}
/>
</>
)}
{isToggle && (
<>
<h3>Events of this clock</h3>
<EventList
clockID={clockID}
eventState={eventState}
deleteEvent={deleteEvent}
updateEvent={updateEvent}
deleteEventsByClockID={deleteEventsByClockID}
/>
</>
)}
</div>
)
}
export default EventAction;
Here is my App Component Code -
import ClockList from "./components/clock-list";
import LocalClock from "./components/local-clock";
import useApp from "./hooks/useApp";
import { localClockInitState } from "./initialStates/clockInitState";
const App = () => {
const {
localClock,
clocks,
updateLocalClock,
createClock,
updateClock,
deleteClock,
getEventsByClockID,
addEvent,
deleteEvent,
updateEvent,
deleteEventsByClockID,
} = useApp(localClockInitState);
return (
<div>
<LocalClock
clock={localClock}
updateClock={updateLocalClock}
createClock={createClock}
/>
<ClockList
clocks={clocks}
localClock={localClock.date}
updateClock={updateClock}
deleteClock={deleteClock}
getEventsByClockID={getEventsByClockID}
addEvent={addEvent}
deleteEvent={deleteEvent}
updateEvent={updateEvent}
deleteEventsByClockID={deleteEventsByClockID}
/>
</div>
)
}
export default App;
and Here is my useApp hook -
import { useState } from "react";
import deepClone from "../utils/deepClone";
import generateID from "../utils/generateId";
import useEvents from "./useEvents";
const getID = generateID('clock');
const useApp = (initValue) => {
const [localClock, setLocalClock] = useState(deepClone(initValue));
const [clocks, setClocks] = useState([]);
const {
// events,
// getEvents,
getEventsByClockID,
addEvent,
deleteEvent,
deleteEventsByClockID,
updateEvent,
} = useEvents();
const updateLocalClock = (data) => {
setLocalClock({
...localClock,
...data,
})
}
const createClock = (clock) => {
clock.id = getID.next().value;
setClocks((prev) => ([
...prev, clock
]))
}
const updateClock = (updatedClock) => {
setClocks(clocks.map(clock => {
if(clock.id === updatedClock.id) return updatedClock;
return clock;
}));
}
const deleteClock = (id) => {
setClocks(clocks.filter(clock => clock.id !== id));
}
return {
localClock,
clocks,
updateLocalClock,
createClock,
updateClock,
deleteClock,
getEventsByClockID,
addEvent,
deleteEvent,
updateEvent,
deleteEventsByClockID,
}
}
export default useApp;
I want to show all events incorporated with each individual clock.
I have a pagination made with a react-paginate package called react-paginate here is the link to the doc. https://www.npmjs.com/package/react-paginate
I have implemented it in my App which is a notes diary, the user creates notes and these are dynamically saved in the localStorage and displayed on screen, well, I have established that there are 6 notes per page, that is, when there is a seventh note, it should not be displayed unless the user goes to page 2, when there are 13 notes page 3 and so ...
The functionality of my component that I have called Pagination works correctly, it is dynamic, I have right now testing 13 notes, so it shows me 3 pages, if I had 12, it would show me 2.
The problem is that although my pagination is correct, the 13 notes are being shown on the screen, when it should be 6 - 6 - 1.
I leave you the code to see if we can find the error, greetings and thanks in advance.
The prop that Pagination receives called data, are basically the notes that are created dynamically in App.js. const [notes, setNotes] = useState([]);
Component Pagination
import React, { useEffect, useState } from 'react'
import ReactPaginate from 'react-paginate';
import '../styles/Pagination.css';
const Pagination = (props) => {
const { data } = props;
// We start with an empty list of items.
const [currentItems, setCurrentItems] = useState([]);
const [pageCount, setPageCount] = useState(0);
// Here we use item offsets; we could also use page offsets
// following the API or data you're working with.
const [itemOffset, setItemOffset] = useState(0);
const itemsPerPage = 6;
useEffect(() => {
// Fetch items from another resources.
const endOffset = itemOffset + itemsPerPage;
console.log(`Loading items from ${itemOffset} to ${endOffset}`);
setCurrentItems(data.slice(itemOffset, endOffset));
setPageCount(Math.ceil(data.length / itemsPerPage));
}, [itemOffset, itemsPerPage, data]);
// Invoke when user click to request another page.
const handlePageClick = (event) => {
const newOffset = (event.selected * itemsPerPage) % data.length;
console.log(
`User requested page number ${event.selected}, which is offset ${newOffset}`
);
setItemOffset(newOffset);
};
return (
<>
<ReactPaginate
breakLabel="..."
nextLabel="next >"
onPageChange={handlePageClick}
pageRangeDisplayed={3}
pageCount={pageCount}
previousLabel="< previous"
renderOnZeroPageCount={null}
containerClassName="pagination"
pageLinkClassName="page-num"
previousLinkClassName="page-num"
nextLinkClassName="page-num"
activeLinkClassName="activee boxx"
/>
</>
);
}
export default Pagination;
Component App
import { useState, useEffect } from "react";
import { nanoid } from 'nanoid';
import NoteList from "./components/NoteList";
import './App.css';
import Search from "./components/Search";
import Header from "./components/Header";
import Pagination from "./components/Pagination";
const App = () => {
const [notes, setNotes] = useState([]);
const [searchText, setSearchText] = useState('');
const [darkMode, setDarkMode] = useState(false);
//Se encarga de mostrar la nota para escribir
const [showNote, setShowNote] = useState(true); //eslint-disable-line
useEffect(() => {
const saveNotes = JSON.parse(localStorage.getItem('notes-data'));
if (saveNotes){
setNotes(saveNotes);
}
}, []);
useEffect(() => {
localStorage.setItem('notes-data', JSON.stringify(notes))
},[notes])
const addNote = (inputText, text) => {
const date = new Date();
const newNote = {
id: nanoid(),
title: inputText,
text: text,
date: date.toLocaleString()
}
const newNotes = [newNote, ...notes];
setNotes(newNotes)
}
const deleteNote = (id) => {
var response = window.confirm("Are you sure that you want to remove the note?");
if (response){
const notesUpdated = notes.filter((note) => note.id !== id)
setNotes(notesUpdated);
}
}
return (
<div className={darkMode ? 'dark-mode' : ''}>
<div className="container">
<Header
handleToggleTheme={setDarkMode}
/>
<Search
handleSearchNote={setSearchText}
setShowNote={setShowNote}
/>
<NoteList
notes={notes.filter((noteText) =>
noteText.title.toLowerCase().includes(searchText)
)}
handleAddNote={addNote}
handleDeleteNote={deleteNote}
/>
<Pagination data={notes}/>
</div>
</div>
)
}
export default App;
The problem is you are not using currentItems and the paginated data is stored in that state.
Codesandbox: https://codesandbox.io/s/sweet-keldysh-2u72vd
Pagination.js
import React, { useEffect, useState } from 'react'
import ReactPaginate from 'react-paginate';
import NoteList from "./components/NoteList";
import '../styles/Pagination.css';
const Pagination = (props) => {
const { data, searchText, handleAddNote, handleDeleteNote } = props;
// We start with an empty list of items.
const [currentItems, setCurrentItems] = useState([]);
const [pageCount, setPageCount] = useState(0);
// Here we use item offsets; we could also use page offsets
// following the API or data you're working with.
const [itemOffset, setItemOffset] = useState(0);
const itemsPerPage = 6;
useEffect(() => {
// Fetch items from another resources.
const endOffset = itemOffset + itemsPerPage;
console.log(`Loading items from ${itemOffset} to ${endOffset}`);
setCurrentItems(data.slice(itemOffset, endOffset));
setPageCount(Math.ceil(data.length / itemsPerPage));
}, [itemOffset, itemsPerPage, data]);
// Invoke when user click to request another page.
const handlePageClick = (event) => {
const newOffset = (event.selected * itemsPerPage) % data.length;
console.log(
`User requested page number ${event.selected}, which is offset ${newOffset}`
);
setItemOffset(newOffset);
};
return (
<>
<NoteList
notes={currentItems.filter((noteText) =>
noteText.title.toLowerCase().includes(searchText)
)}
handleAddNote={handleAddNote}
handleDeleteNote={handleDeleteNote}
/>
<ReactPaginate
breakLabel="..."
nextLabel="next >"
onPageChange={handlePageClick}
pageRangeDisplayed={3}
pageCount={pageCount}
previousLabel="< previous"
renderOnZeroPageCount={null}
containerClassName="pagination"
pageLinkClassName="page-num"
previousLinkClassName="page-num"
nextLinkClassName="page-num"
activeLinkClassName="activee boxx"
/>
</>
);
}
export default Pagination;
App.js
import { useState, useEffect } from "react";
import { nanoid } from 'nanoid';
import './App.css';
import Search from "./components/Search";
import Header from "./components/Header";
import Pagination from "./components/Pagination";
const App = () => {
const [notes, setNotes] = useState([]);
const [searchText, setSearchText] = useState('');
const [darkMode, setDarkMode] = useState(false);
//Se encarga de mostrar la nota para escribir
const [showNote, setShowNote] = useState(true); //eslint-disable-line
useEffect(() => {
const saveNotes = JSON.parse(localStorage.getItem('notes-data'));
if (saveNotes){
setNotes(saveNotes);
}
}, []);
useEffect(() => {
localStorage.setItem('notes-data', JSON.stringify(notes))
},[notes])
const addNote = (inputText, text) => {
const date = new Date();
const newNote = {
id: nanoid(),
title: inputText,
text: text,
date: date.toLocaleString()
}
const newNotes = [newNote, ...notes];
setNotes(newNotes)
}
const deleteNote = (id) => {
var response = window.confirm("Are you sure that you want to remove the note?");
if (response){
const notesUpdated = notes.filter((note) => note.id !== id)
setNotes(notesUpdated);
}
}
return (
<div className={darkMode ? 'dark-mode' : ''}>
<div className="container">
<Header
handleToggleTheme={setDarkMode}
/>
<Search
handleSearchNote={setSearchText}
setShowNote={setShowNote}
/>
<Pagination data={notes} handleAddNote={addNote}
handleDeleteNote={deleteNote} searchText={searchText} />
</div>
</div>
)
}
export default App;
I am using react-select with more than 20,000 options fetched from the database via Node API.
Page was not even loading .
Now, I added "react-select-async-pagination".
But the data is fetched once only.
import React, { useRef, useState, useEffect } from "react";
import Select from "react-select";
import LoadOptions from "./LoadOptions";
import { AsyncPaginate } from "react-select-async-paginate";
const TooManySelect = () => {
const [value, onChange] = useState(null);
return (
<div className="Select-options">
<label>Pdt code</label>
<AsyncPaginate
defaultOptions
value={value}
loadOptions={LoadOptions}
onChange={onChange}
/>
</div>
);
};
export default TooManySelect;
LoadOptions : Here is the api call. I am passing the count of the last row fetched every time via "prevLast" so that I can use the OFFSET prevLast ROWS inside database query.
import { useState } from "react";
const sleep = (ms) =>
new Promise((resolve) => {
setTimeout(() => {
resolve();
}, ms);
});
const baseurl = "http://localhost:5000/api";
const LoadOptions = async (search, prevOptions) => {
const [prevLast, setPrevLast] = useState(0);
const [pdtOpt, setPdtOpt] = useState([]);
await sleep(1000);
const response = await fetch(`${baseurl}/pdt/${prevLast}`);
const pList = await response.json();
const pdtList = [];
for (let i = 0; i < pList.length; i++) {
pdtList.push({ label: pList[i].pdtno, value: pList[i].pdtno });
}
setPdtOpt(pdtList);
setPrevLast(pList.length);
return {
options: pdtList,
hasMore: true
};
};
export default LoadOptions;
Here is my codesandbox link.
https://codesandbox.io/s/react-select-paginate-test-tob90j
My question is : How can we access thousands of (select) options from DB without page freeze?
I got this link while googleing.
https://blog.saeloun.com/2022/03/03/infinite-scroll-with-pagination.html#using-lazy-loading-and-pagination
It helped me came to a perfect solution.
So my current code goes like this
(Made only a few changes from the code given in the above link).
Select component:
import React, { useState } from "react";
import SelectWrapper from "./SelectWrapper";
const baseurl = "http://localhost:5000/api";
function MainSelect() {
const [options, setOptions] = useState([]);
const [selectedOption, setSelectedOption] = useState("");
const [pageNo, setPageNo] = useState(0);
const [hasNextPage, setHasNextPage] = useState(true);
const [isNextPageLoading, setIsNextPageLoading] = useState(false);
const loadOptions = async (page) => {
try {
// console.log(`Page ${page}`);
const size = 50;
setIsNextPageLoading(true);
const data = await fetch(`${baseurl}/pdt/${page}/${size}`);
const pList = await data.json();
const pdtList = [];
for (let i = 0; i < pList.length; i++) {
pdtList.push({ label: pList[i].pdtno, value: pList[i].pdtno });
}
setOptions(pdtList);
setIsNextPageLoading(false);
setHasNextPage(pdtList.length < 500);
setPageNo(page);
} catch (err) {
console.log(err);
}
};
console.log(options);
const loadNextPage = async () => {
await loadOptions(pageNo + 1);
};
return (
<div className="dropdown">
<div className="dropdown">
<div className="label">
<label>Pdt</label>
</div>
<SelectWrapper
value={selectedOption}
placeholder="Select"
isClearable
hasNextPage={hasNextPage}
isNextPageLoading={isNextPageLoading}
options={options}
loadNextPage={loadNextPage}
onChange={(selected) => setSelectedOption(selected)}
/>
</div>
</div>
);
}
export default MainSelect;
SelectWrapper that carries out the Virtualization part:
import React, { useEffect, useState } from "react";
import { FixedSizeList as List } from "react-window";
import InfiniteLoader from "react-window-infinite-loader";
import Select from "react-select";
import AutoSizer from "react-virtualized-auto-sizer";
const SelectWrapper = (props) => {
const {
hasNextPage,
isNextPageLoading,
options,
loadNextPage,
placeholder,
onChange,
value,
} = props;
const [selectedOption, setSelectedOption] = useState(value);
useEffect(() => {
setSelectedOption(value);
}, [value]);
const itemCount = hasNextPage ? options.length + 1 : options.length;
const loadMoreItems = isNextPageLoading ? () => {} : loadNextPage;
const isItemLoaded = (index) => !hasNextPage || index < options.length;
const MenuList = ({ children }) => {
const childrenArray = React.Children.toArray(children);
const Item = ({ index, style, ...rest }) => {
const child = childrenArray[index];
return (
<div
className="drop-down-list"
style={{
borderBottom: "1px solid rgb(243 234 234 / 72%)",
display: "flex",
alignItems: "center",
...style,
}}
onClick={() => handleChange(options[index])}
{...rest}
>
{isItemLoaded(index) && child ? child : `Loading...`}
</div>
);
};
return (
<AutoSizer disableHeight>
{({ width }) => (
<InfiniteLoader
isItemLoaded={(index) => index < options.length}
itemCount={itemCount}
loadMoreItems={loadMoreItems}
>
{({ onItemsRendered, ref }) => (
<List
className="List"
height={150}
itemCount={itemCount}
itemSize={35}
onItemsRendered={onItemsRendered}
ref={ref}
width={width}
overscanCount={4}
>
{Item}
</List>
)}
</InfiniteLoader>
)}
</AutoSizer>
);
};
const handleChange = (selected) => {
console.log("test");
onChange(selected);
};
return (
<Select
placeholder={placeholder}
components={{ MenuList }}
value={selectedOption}
options={options}
{...props}
/>
);
};
export default SelectWrapper;
For anyone who needs the same, I have updated the codepen as well.
https://codesandbox.io/s/react-select-paginate-test-tob90j
I am trying to make kind of flash message displayer to display success, error, warning messages at the top for a certain duration.
I have made the use of useRef hook to store timeouts so that I can clear it incase component unmounts before timeout completion.
Everything works as expected except, if the component unmounts before timeout callback, it does not clear the timeout which indeed is trying to setState which gives
Warning: Can't perform a React state update on an unmounted component
import React, { useEffect, useRef, useState } from 'react'
import SuccessGreen from '../../assets/SuccessGreen.svg'
import Cross from '../../assets/Cancel.svg'
import WarningExclamation from '../../assets/WarningExclamation.svg'
const ICONS_MAP = {
"warning": WarningExclamation,
"success": SuccessGreen,
"error": ""
}
export const FlashMessages = ({
duration=5000,
closeCallback,
pauseOnHover=false,
messageTheme='warning',
typoGraphy={className: 'text_body'},
firstIcon=true,
...props
}) => {
const [isDisplayable, setIsDisplayable] = useState(true)
const resumedAt = useRef(null)
const remainingDuration = useRef(duration)
const countDownTimer = useRef(null)
useEffect(() => {
countDownTimer.current = resumeDuration()
console.log(countDownTimer, "From mount")
return () => {clearTimeout(countDownTimer.current)}
}, [])
const resumeDuration = () => {
clearTimeout(countDownTimer.current)
resumedAt.current = new Date()
return setTimeout(() => forceCancel(), remainingDuration.current)
}
const pauseDuration = () => {
if(pauseOnHover){
clearTimeout(countDownTimer.current)
remainingDuration.current = remainingDuration.current - (new Date() - resumedAt.current)
}
}
const forceCancel = () => {
console.log(countDownTimer, "From force")
clearTimeout(countDownTimer.current);
setIsDisplayable(false);
closeCallback(null);
}
return isDisplayable ? (
<div onMouseEnter={pauseDuration} onMouseLeave={resumeDuration}
className={`flash_message_container ${messageTheme} ${typoGraphy.className}`} style={props.style}>
{ firstIcon ? (<img src={ICONS_MAP[messageTheme]} style={{marginRight: 8, width: 20}} />) : null }
<div style={{marginRight: 8}}>{props.children}</div>
<img src={Cross} onClick={forceCancel} style={{cursor: 'pointer', width: 20}}/>
</div>
):null
}
I have tried to mimic the core functionality of this npm package
https://github.com/danielsneijers/react-flash-message/blob/master/src/index.jsx
but whith functional component.
I think the problem is that when the mouseleave event happens, the timeout id returned by resumeDuration is not saved in countDownTimer.current, so the timeout isn't cleared in the cleanup function returned by useEffect.
You could modify resumeDuration to save the timeout id to countDownTimer.current instead of returning it:
countDownTimer.current = setTimeout(() => forceCancel(), remainingDuration.current)
and then, inside useEffect, just call resumeDuration, so the component would look like this:
import React, { useEffect, useRef, useState } from 'react'
import SuccessGreen from '../../assets/SuccessGreen.svg'
import Cross from '../../assets/Cancel.svg'
import WarningExclamation from '../../assets/WarningExclamation.svg'
const ICONS_MAP = {
"warning": WarningExclamation,
"success": SuccessGreen,
"error": ""
}
export const FlashMessages = ({
duration=5000,
closeCallback,
pauseOnHover=false,
messageTheme='warning',
typoGraphy={className: 'text_body'},
firstIcon=true,
...props
}) => {
const [isDisplayable, setIsDisplayable] = useState(true)
const resumedAt = useRef(null)
const remainingDuration = useRef(duration)
const countDownTimer = useRef(null)
useEffect(() => {
resumeDuration()
console.log(countDownTimer, "From mount")
return () => {clearTimeout(countDownTimer.current)}
}, [])
const resumeDuration = () => {
clearTimeout(countDownTimer.current)
resumedAt.current = new Date()
countDownTimer.current = setTimeout(() => forceCancel(), remainingDuration.current)
}
const pauseDuration = () => {
if(pauseOnHover){
clearTimeout(countDownTimer.current)
remainingDuration.current = remainingDuration.current - (new Date() - resumedAt.current)
}
}
const forceCancel = () => {
console.log(countDownTimer, "From force")
clearTimeout(countDownTimer.current);
setIsDisplayable(false);
closeCallback(null);
}
return isDisplayable ? (
<div onMouseEnter={pauseDuration} onMouseLeave={resumeDuration}
className={`flash_message_container ${messageTheme} ${typoGraphy.className}`} style={props.style}>
{ firstIcon ? (<img src={ICONS_MAP[messageTheme]} style={{marginRight: 8, width: 20}} />) : null }
<div style={{marginRight: 8}}>{props.children}</div>
<img src={Cross} onClick={forceCancel} style={{cursor: 'pointer', width: 20}}/>
</div>
):null
}
and it would then mimic the logic from https://github.com/danielsneijers/react-flash-message/blob/master/src/index.jsx
I have modal which shows when user visit my page now I want a user to be able to hide this modal so that doesn't show the modal again using local storage or cookies
Here is a live demo on code sandbox : dont show me again
Js code
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import Modal from "./Modal";
import useModal from "./useModal";
import "./styles.css";
const App = () => {
const { isShowing, toggle } = useModal();
const [cookieConsent, showCookieConsent] = useState(true);
const [checked, setIsChecked] = useState(0);
const handleOnchange = (e) => {
setIsChecked(e.target.value);
};
let modalStorage = localStorage.setItem("hide", checked);
useEffect(() => {
toggle();
if (modalStorage) {
showCookieConsent(false);
}
}, []);
const clearStorage = () => {
localStorage.clear();
};
return (
<div className="App">
<button onClick={clearStorage}> Clear Storage</button>
{cookieConsent === false && (
<Modal
isShowing={isShowing}
handleOnchange={handleOnchange}
hide={toggle}
/>
)}
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Now when I click the checkbox and close the modal and I refresh the page it shows again instead of hiding it
What am I doing wrong here?
I changed a few things
const handleOnchange = (e) => {
setIsChecked(e.target.checked);
localStorage.setItem("hide", e.target.checked)
};
useEffect(() => {
toggle();
let modalStorage = localStorage.getItem("hide", checked);
if (modalStorage) {
showCookieConsent(false);
}
}, []);
I've used a custom hook to solve a similar issue. Sharing the code in case it helps anyone.
// useInfoBanner.jsx
import { useEffect, useState } from "react";
import { getFromLocalStorage, setToLocalStorage } from "utils/storage";
const useInfoBanner = (key) => {
const [showInfoBanner, setShowInfoBanner] = useState(false);
const handleHideInfoBanner = () => {
setShowInfoBanner(false);
setToLocalStorage(key, true);
};
useEffect(() => {
const isHideInfoBanner = getFromLocalStorage(key);
isHideInfoBanner ? setShowInfoBanner(false) : setShowInfoBanner(true);
}, []);
return [showInfoBanner, handleHideInfoBanner];
};
export default useInfoBanner;
// utils/storage.js
export const getFromLocalStorage = key =>JSON.parse(localStorage.getItem(key));
export const setToLocalStorage = (key, value) =>
localStorage.setItem(key, JSON.stringify(value));
// You can use this in your components like so
const [showInfoBanner, handleHideInfoBanner] = useInfoBanner("hideSettingsInfoBanner");
//.. then in return
{showInfoBanner && <InfoBanner />}