react clearinterval not working for another function - reactjs

i am working on one of the react project, for that i have used setInterval and clearInterval, it works fine in same function, but when i used clearInterval in another function it is not working, can anyone please help me why it is happening like that ? here i have attached my whole code, can anyone please look my code and help me to resolve this issue ? clearInterval is not working for the onClickStartRound in function
export class GamePlayBlack extends React.Component<Props, State>
{
public interval_counter;
public setTimer = '30:00';
public setSeconds = 30;
constructor(props: Props)
{
super(props);
this.state = {
gameData: GameDataStore.state,
userData: UserDataStore.state,
buttonLoading: false,
gameStarted: false,
updateTimer: this.setTimer,
clearintervaltimer : '',
};
localStorage.setItem('is_called','0');
localStorage.setItem('is_called_show_winner_card','0');
}
public componentDidMount(): void
{
GameDataStore.listen(data => this.setState({
gameData: data
}));
UserDataStore.listen(data => this.setState({
userData: data
}));
}
private onSelect = (winningPlayerGuid: string) =>
{
return GameDataStore.chooseWinner(this.state.userData.playerGuid, winningPlayerGuid);
};
private onClickStartRound = () =>
{
localStorage.setItem('is_started','1');
clearInterval(this.interval_counter);
this.setState({
buttonLoading: true,
gameStarted: true,
});
GameDataStore.startRound(this.state.userData.playerGuid)
.finally(() => this.setState({
buttonLoading: false,
gameStarted: true,
}));
};
private onClickSkipBlack = () =>
{
this.setState({
buttonLoading: true
});
GameDataStore.skipBlack(this.state.userData.playerGuid)
.finally(() => this.setState({
buttonLoading: false
}));
};
private waitforstart = (game_id:any,playerGUID:any,chooserGuid:any) => { //game_id:any,playerGUID:any,chooserGuid:any
console.log("guid");
console.log(this.state.gameData);
console.log(this.state.userData);
let self = this;
window.setInterval(function() {
//alert("sdsd");
return GameDataStore.removePlayer(game_id,playerGUID,chooserGuid);
}.bind(self),30000);
};
private skipPlayer = (game_string_id:any,target_turn:any,chooserGuid:any) => {
return GameDataStore.skipPlayer(game_string_id,target_turn,chooserGuid);
}
public startTimer = () => {
localStorage.setItem('is_called','1');
let timer = this.setSeconds, minutes, seconds;
let _self = this;
this.interval_counter = setInterval(function () {
let is_started = localStorage.getItem('is_started');
if(is_started == "0") {
let chooserGuid = localStorage.getItem('chooserGuid');
let game_string_id = localStorage.getItem('game_id');
let target_turn = localStorage.getItem('target_turn');
let is_called = localStorage.getItem('is_called');
if(typeof timer!==undefined && timer!=null) {
minutes = parseInt(timer/60 as any,10);
seconds = parseInt(timer%60 as any,10);
minutes = minutes < 10 ? "0" + minutes : minutes;
seconds = seconds < 10 ? "0" + seconds : seconds;
console.log(minutes + ":" + seconds);
_self.setState({
updateTimer: minutes + ":" + seconds
});
if (--timer < 0) {
//localStorage.setItem('is_called','0');
if(is_started == "0") {
_self.skipPlayer(game_string_id,target_turn,chooserGuid);
clearInterval(_self.interval_counter);
return false;
}
}
}
}
}, 1000);
}
public render()
{
const {
gameData,
buttonLoading
} = this.state;
const me = gameData.game?.players?.[this.state.userData.playerGuid];
const defsLoaded = cardDefsLoaded(gameData);
if (!me || !gameData.game || !defsLoaded)
{
return <ContainerProgress />;
} else {
let game_id = gameData.game.id;
let chooserGuid = gameData.game.chooserGuid;
let all_players = gameData.game.players;
let all_player_id = Object.keys(all_players);
//alert(gameData.game.chooserGuid);
let filteredAry = all_player_id.filter(e => e !== this.state.userData.playerGuid);
console.log("guid:"+chooserGuid);
console.log("all players:"+all_player_id);
console.log("new array:"+filteredAry);
let target_item = filteredAry.find((_, i, ar) => Math.random() < 1 / (ar.length - i));
if(typeof target_item !== undefined && target_item!=null) {
localStorage.setItem('target_turn',target_item);
}
}
const {
players,
chooserGuid,
roundCards,
roundStarted,
} = gameData.game;
const roundCardKeys = Object.keys(roundCards ?? {});
const remainingPlayerGuids = Object.keys(players ?? {})
.filter(pg => !(pg in (roundCards ?? {})) && pg !== chooserGuid);
const remainingPlayers = remainingPlayerGuids.map(pg => unescape(players?.[pg]?.nickname));
const revealedIndex = this.state.gameData.game?.revealIndex ?? 0;
const timeToPick = remainingPlayers.length === 0;
const revealMode = timeToPick && revealedIndex < roundCardKeys.length;
const hasWinner = !!gameData.game?.lastWinner;
if(!roundStarted) {
//alert(chooserGuid);
localStorage.setItem('is_started','0');
if(typeof gameData.game.id !== undefined && gameData.game.id!=null) {
localStorage.setItem('game_id',gameData.game.id);
}
if(typeof gameData.game.chooserGuid !== undefined && gameData.game.chooserGuid!=null) {
localStorage.setItem('chooserGuid',gameData.game.chooserGuid);
}
let is_called = localStorage.getItem('is_called');
if(is_called == "0") {
this.startTimer();
}
} else {
localStorage.setItem('is_started','1');
}
return (
<>
<div>
<PlayersRemaining/>
</div>
<Divider style={{margin: "1rem 0"}}/>
{!roundStarted && (
<Typography style={{marginBottom: "1rem", textAlign: "center"}}>
<strong>You are the Card Captain!</strong>
<br/>
Read the card aloud, then click Start The Round. Once everyone plays, you will choose your favorite!<br/>
<span> {this.state.updateTimer}</span> Seconds
</Typography>
)}
{!timeToPick && roundStarted && (
<CardPlayTimeRemaining gameData={gameData}/>
)}
<Grid container spacing={2} style={{justifyContent: "center", marginTop: "1rem"}}>
{(!hasWinner) && (
<Grid item xs={12} sm={6} md={4} lg={3}>
<BlackCard packId={gameData.game?.blackCard.packId}>
{gameData.blackCardDef?.content}
</BlackCard>
</Grid>
)}
<RevealWhites canReveal={true}/>
<ShowWinner/>
</Grid>
{!roundStarted && (
<div style={{marginTop: "1rem", textAlign: "center"}}>
<LoadingButton loading={buttonLoading} color={"secondary"} variant={"outlined"} onClick={this.onClickSkipBlack}>
Skip Card
</LoadingButton>
<LoadingButton loading={buttonLoading} color={"secondary"} variant={"contained"} onClick={this.onClickStartRound} style={{marginLeft: "1rem"}}>
Start the round!
</LoadingButton>
</div>
)}
<PickWinner
canPick={true}
hasWinner={hasWinner}
onPickWinner={this.onSelect}
revealMode={revealMode}
timeToPick={timeToPick}
/>
</>
);
}
}

Related

AnimatedFlatList jumped to the first of list when fetchNextPage called

In onEndReached event I check for next page and if it exits I call fetchNextPage() of useInfiniteQuery.
but just after that my FlatList jumped into the first of the list.
const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);
return (
<AnimatedFlatList
{...props}
onScroll={onScroll({y}, {height}, {height: layoutH})}
onEndReached={({distanceFromEnd}) => {
if (distanceFromEnd < 0) {
return;
}
if (hasNextPage && !isFetchingNextPage) {
fetchNextPage();
}
}}
/>
);
and here is my useInfiniteQuery code:
const {
data,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
} = useInfiniteQuery(
[gate.getDiscover2.name, request],
({pageParam = 1}) =>
gate.getDiscover2({...request, discover_page: pageParam}),
{
getNextPageParam: lastPage => {
const {current_page, last_page} = lastPage.data || {};
return current_page < last_page ? current_page + 1 : false;
},
},
);

Please tell me how to save the changed value in redux store

I use usestate in the page to change the screen displayed when scrolling the html tag by adding a value of data.
And when I leave the page, it is saved in the store to save the page scroll value and data.
When you first save and return to that page, it returns to the desired scrolling position, but in that state, after scrolling and changing data, an error occurs to another page.
I copied and used all the values ​​stored in the store, but I don't know why it detects a mutation.
Is it not possible to change the data to usestate in the page?
I don't know which part is causing the problem. Help.
const [mainPage, setMainPage] = useState(store.mainPage.list.length ? store.mainPage : {list: [], scroll: 0});
const createScrollStopListener = (element, callback, timeout) => {
console.log('createScrollStopListener :: ');
if(document.querySelector('.body').scrollTop < 1) {
document.querySelector('.body').scrollTo({top: 1 , left: 0, behavior: "auto"});
}
let removed = false;
let handle = null;
const onScroll = () => {
if (handle) {
clearTimeout(handle);
}
handle = setTimeout(callback, timeout || 20 // default 200 ms
};
element.addEventListener('scroll', onScroll);
return () => {
if (removed) {
return;
}
removed = true;
if (handle) {
clearTimeout(handle);
}
element.removeEventListener('scroll', onScroll);
};
};
useEffect(() => {
let body = document.querySelector(".body")
body.addEventListener("scroll", function() {
mainThumbnailListDraw();
})
const destroyListener = createScrollStopListener(document.querySelector('.body'), () => {
console.log('createScrollStopListener ing... ::');
changeVideoImageTag();
});
return () => destroyListener(); // when App component is unmounted
}, [mainPage.list]);
const changeVideoImageTag = () => {
const mainPageCopy = {
...store.mainPage,
scroll: store.mainPage.scroll !== 0 ? store.mainPage.scroll : 0,
list: [
...mainPage.list,
],
};
console.log('changeVideoImageTag :: ', mainPageCopy);
let windowHeight = window.innerHeight
let thumbnailItem = document.querySelectorAll(".mainThumbnailItemImg");
thumbnailItem.forEach(function (value, index) {
let videoClientTop = value.getBoundingClientRect().top;
let videoClientHeight = value.getBoundingClientRect().height;
let valueIndex = mainPageCopy.list[index];
value.autoplay = true;
value.loop = true;
value.muted = true;
if(valueIndex) {
if (videoClientTop <= windowHeight && videoClientTop >= -videoClientHeight) {
// console.log('check :: ', index)
if (valueIndex.type == "VIDEO") {
// console.log('is_video? :: ', index)
let videoData = JSON.parse(valueIndex.vimeo_files);
valueIndex.videoTag = `<div className="mainThumbnailItemVideo">
<video class="mainThumbnailItemVideo" autoplay="" muted="" playsinline="" loop preload="metadata"data-wf-ignore="true" data-object-fit="cover" src=${videoData.fileList && videoData.fileList[0].url} type="video/mp4"/></div>`;
}
else {
valueIndex.videoTag = ""
}
}else {
valueIndex.videoTag = ""
}
}
})
setMainPage(mainPageCopy);
}
function mainThumbnailListDraw() {
console.log('그려진드앙 mainThumbnailListDraw :: ', mainPage);
let listJsx = mainPage.list.map(function (value, index) {
let videoData = '';
let videoPreview = '';
let imageData = '';
if (value.type == "VIDEO") {
videoData = JSON.parse(value.vimeo_files);
if(videoData.preview.url) {
let is_small = videoData.preview.url.split('/')
let num = is_small.length-1;
let result = is_small.splice(num,0,'scaled-w_600')
videoPreview = is_small.join('/');
}
} else if(value.type == "IMG") {
let is_small = value.image_url.split('/');
let num = is_small.indexOf('scaled-w_1000')
let result = is_small.splice(num,1,'scaled-w_600')
imageData = is_small.join('/');
} else {
return
}
return (
<li className="mainThumbnailItem" key={index} onClick={() => {
const mainPageCopy = {
...store.mainPage,
scroll: document.querySelector('.body').scrollTop,
list: [
...mainPage.list,
...store.mainPage.list.slice(mainPageList.length),
],
};
dispatch({ type: "mainPage/set", payload: mainPageCopy });
router.push(`/product/${value.product_seq}`);
}} ref={ref}>
<LazyLoadImage wrapperClassName="mainThumbnailItemImg" effect="opacity" src={videoData ? videoPreview : imageData} style={{position: "absolute", top: "0", left: "0", width: "100%", height: "100%", objectFit: "cover"}}></LazyLoadImage>
{value.videoTag ? <div dangerouslySetInnerHTML={{__html: `${value.videoTag}`}}/> : <></>}
<button className={`mainThumbnailItemWish wishButtonImg ${value.is_wish == true ? " is_wish" : ""}`} value={value.is_wish} type="button" onClick={(e) => {loginData.client_type ? (e.stopPropagation(), changeWishButton(e, value.product_seq, index)) : (e.stopPropagation(), script.userBlock(dispatch, router))}}/>
</li>
);
});
return listJsx;
}
Doing ...mainPage.list is not enough to clone the state, you need to clone the objects deep inside the list as well. You can either do it manually with the spread operator, or use one of those deep copy libraries (there are several on npm). The lazy dev's workaround is to use JSON.stringify together with JSON.parse, but that's slow.

having trouble in getting data from local storage and displaying that data on the same page

As I'm new to ReactJS, I'm looking to get the data stored in the local storage and display it, basically its a timer, when we enter or set value in the input field, data is stored in the local storage so that on page reload it should'nt loose the data, now i want that data to be displayed in the fieldset present at the end of the code with id="lsOutput", which will get rendered only on button click of id="btnInsert" , any idea how to solve this?
This is the code
class Timer extends Component {
data;
constructor(props) {
super(props);
this.inputHandler = this.inputHandler.bind(this);
this.timerSubmit = this.timerSubmit.bind(this);
this.getData = this.getData.bind(this);
this.state = {
hours: 0,
minutes: 0,
seconds:0
}
this.hoursInput = React.createRef();
this.minutesInput= React.createRef();
this.secondsInput = React.createRef();
}
inputHandler = (e) => {
this.setState({[e.target.name]: e.target.value});
}
timerSubmit = (e) => {
e.preventDefault()
localStorage.setItem('key',JSON.stringify(this.state));
}
getData = (e) => {
e.preventDefault()
console.log( localStorage.getItem('key',JSON.parse(this.state)));
}
componentDidMount() {
this.data = JSON.parse(localStorage.getItem('key'));
if (localStorage.getItem('key')) {
this.setState({
hours: this.data.hours,
minutes: this.data.minutes,
seconds: this.data.seconds
})
} else {
this.setState({
hours: '',
minutes: '',
seconds: ''
})
}
}
convertToSeconds = ( hours, minutes,seconds) => {
return seconds + minutes * 60 + hours * 60 * 60;
}
startTimer = () => {
this.timer = setInterval(this.countDown, 1000);
}
countDown = () => {
const { hours, minutes, seconds } = this.state;
let c_seconds = this.convertToSeconds(hours, minutes, seconds);
if(c_seconds) {
// seconds change
seconds ? this.setState({seconds: seconds-1}) : this.setState({seconds: 59});
// minutes change
if(c_seconds % 60 === 0 && minutes) {
this.setState({minutes: minutes -1});
}
// when only hours entered
if(!minutes && hours) {
this.setState({minutes: 59});
}
// hours change
if(c_seconds % 3600 === 0 && hours) {
this.setState({hours: hours-1});
}
} else {
clearInterval(this.timer);
}
}
stopTimer = () => {
clearInterval(this.timer);
}
resetTimer = () => {
this.setState({
hours: 0,
minutes: 0,
seconds: 0
});
this.hoursInput.current.value = "00";
this.minutesInput.current.value = "00";
this.secondsInput.current.value = "00";
}
render() {
const { hours, minutes, seconds } = this.state;
const inphr = document.getElementById("inphr");
const inpmin = document.getElementById("inpmin");
const btnInsert = document.getElementById("btnInsert");
const lsOutput = document.getElementById("lsOutput");
window.onload = function(){
btnInsert.onclick = () => {
const key = inphr.value;
const value = inpmin.value;
if (key && value) {
localStorage.setItem(key, value);
window.localStorage.reload();
}
};
for (let i = 0; i < localStorage.length; i++ ){
const key = localStorage.key(i);
const value = localStorage.getItem(key);
lsOutput.HTML += `${key}: ${value}<br />`;
}
}
return (
<div className="App">
<div className="inputGroup" onChange={this.timerSubmit}>
<input id="inphr" className="timerinput" ref={this.hoursInput} type="text" placeholder={"00"} name="hours" onChange={this.inputHandler} /><span className="colan">:</span>
<input id="inpmin" className="timerinput" ref={this.minutesInput} type="text" placeholder={"00"} name="minutes" onChange={this.inputHandler} /><span className="colan">:</span>
<input id="inpsec" className="timerinput" ref={this.secondsInput} type="text" placeholder={"00"} name="seconds" onChange={this.inputHandler} />
</div>
<div className="timerbtn" >
<FontAwesomeIcon onClick={this.startTimer} className="start" icon={ faPlayCircle }/>
<FontAwesomeIcon onClick={this.stopTimer} className="stop" icon={ faPauseCircle }/>
<FontAwesomeIcon onClick={this.resetTimer} className="reset" icon={ faUndoAlt }/>
</div>
<h1 className="timercd" > {hours}:{minutes}:{seconds} </h1>
<button id="btnInsert">submit</button>
<label ref={this.hoursInput} id="labelhrs">
</label>
<fieldset>
<legend>
Timer
</legend>
<div id="lsOutput" >
</div>
</fieldset>
</div>
);
}
}
export default Timer;

Browser based countdown timer alarm not always keeping perfect time when browser is in the background

The title pretty much says it all, but I'll include my somewhat complicated React timer code anyway.
My app is basically an alarm app, and it keeps perfect time most (about 3/4s) of the time. But sometimes when it is in a background tab, the timing is off (15 minutes could take 20 minutes, for example).
So I'm wondering if this is a common problem that anyone knows about. Does my browser send the clock to sleep to save energy perhaps?
I see other browser based alarm clocks on the web, but I haven't been able to find much about this problem.
Thanks,
class Clock extends React.Component {
render() {
return (
<>
<div className="floatLeft">
<div id="timer">
<Countdown updateDB={this.props.updateDB} taskBarOpen={this.props.taskBarOpen} pauseForModal={this.props.pauseForModal} cycle={this.props.cycle} taskBarCounter={this.props.taskBarCounter}>
</Countdown>
</div>
</div>
</>
);
}
}
function Countdown(props) {
const [timer, setTimer] = React.useState({
name: 'timer',
onBreak: false,
firstStart: true,
isPaused: true,
pauseForModal: false,
time: 0,
timeRemaining: 0,
timerHandler: null,
cycle: 0,
timeEqualsTimeRemaning: true,
showToolbar: false
})
const [taskBarCounter] = React.useState({
counter: 0
})
let taskBarProps = props.taskBarCounter
let allowCountdownRestart = (taskBarCounter.counter !== taskBarProps) ? true : false
const context = React.useContext(ApiContext);
let breakDurations = context.prefs
React.useEffect(() => {
if (allowCountdownRestart) {
taskBarCounter.counter = taskBarCounter.counter + 1
allowCountdownRestart = false
} else {
allowCountdownRestart = true
}
}, [props, allowCountdownRestart])
React.useEffect(() => {
if (props.pauseForModal) {
timer.pauseForModal = true
// handlePause()
} else {
setTimeout(() => {
timer.pauseForModal = false
// handleStart()
}, 300);
}
}, [props.pauseForModal])
React.useEffect(() => {
if (allowCountdownRestart) {
if (!timer.pauseForModal) {
setTimer((timer) => ({
...timer,
time: props.cycle * 60,
timeRemaining: props.cycle * 60,
cycle: props.cycle,
onBreak: false
}));
}
if (timer.isPaused) {
// timer.isPaused = false;
}
}
}, [props])
React.useEffect(() => {
if (timer.time === 0 && !timer.firstStart) {
setTimeout(function () {
if (timer.onBreak) {
playBark()
timer.showToolbar = false
timer.onBreak = false
} else {
const breakDuration = breakDurations[timer.cycle] * 60
playTweet()
if (breakDuration !== 0) {
// playTweet()
setTimer((timer) => ({
...timer,
onBreak: true,
time: breakDuration,
timeRemaining: breakDuration
}));
} else {
// playBark()
timer.showToolbar = false
}
props.updateDB(timer.cycle)
}
}, 1000);
} else {
if (timer.time === timer.timeRemaining) {
timer.firstStart = false
timer.showToolbar = true
handleStart()
}
}
}, [timer.time, timer.time === timer.timeRemaining])
React.useEffect(() => {
if (timer.timeRemaining === 0) {
clearInterval(timer.timerHandler)
setTimer((timer) => ({
...timer,
time: 0,
isPaused: true
}));
}
}, [timer.timeRemaining])
const updateTimeRemaining = e => {
setTimer(prev => {
return { ...prev, timeRemaining: prev.timeRemaining - 1 }
})
}
const handleStart = e => {
if (timer.time !== 0) {
clearInterval(timer.timerHandler)
const handle = setInterval(updateTimeRemaining, 1000);
setTimer({ ...timer, isPaused: false, timerHandler: handle })
}
}
const handlePause = e => {
clearInterval(timer.timerHandler)
setTimer({ ...timer, isPaused: true })
}
const timeFormat = (duration) => {
if (duration > 0) {
var hrs = ~~(duration / 3600);
var mins = ~~((duration % 3600) / 60);
var secs = ~~duration % 60;
var ret = "";
if (hrs > 0) {
ret += "" + hrs + ":" + (mins < 10 ? "0" : "");
}
ret += "" + mins + ":" + (secs < 10 ? "0" : "");
ret += "" + secs;
return ret;
} else {
return "00:00"
}
}
const handleSkip = () => {
clearInterval(timer.timerHandler)
setTimer({ ...timer, timeRemaining: 0 })
}
const handleStop = () => {
clearInterval(timer.timerHandler)
setTimer({ ...timer, onBreak: true, cycle: 0, timeRemaining: 0 })
}
const [playBark] = useSound(bark,
{ volume: 0.35 }
);
const [playTweet] = useSound(tweet,
{ volume: 0.20 }
);
const [playGong] = useSound(gong,
{ volume: 0.20 }
);
return <React.Fragment>
{timer.onBreak ?
<div><h2 className="display-timer-header">On Break </h2> <h2 className="display-timer">{timeFormat(timer.timeRemaining)}</h2></div>
: <div><h3 className="display-timer-header"> Time Left </h3> <h3 ref={context.timerRef} className="display-timer">{timeFormat(timer.timeRemaining)}</h3></div>}
<div className="toolbar-container">
<div className={`toolbar-icons ${props.taskBarOpen ? "taskbar-open" : ""}`}>
<i className="tooltip"><Stop className="toolbar-icon" onClick={handleStop}></Stop>
<span className="tooltiptext">Stop</span></i>
{!timer.isPaused ?
<i className="tooltip pause"><PausePresentation className="toolbar-icon" onClick={handlePause}></PausePresentation>
<span className="tooltiptext pause-tooltip">Pause</span></i>
:
<i className="tooltip pause"><PlayCircleOutline className="toolbar-icon" onClick={handleStart}></PlayCircleOutline>
<span className="tooltiptext">Start</span></i>
}
<i className="tooltip"><SkipNext className="toolbar-icon" onClick={handleSkip} ></SkipNext>
<span className="tooltiptext">Skip to Break</span></i>
</div>
</div>
</React.Fragment>
}
export default Clock;

useEffect() triggers components re-render in one function but not in the other one. Both function DO change state. What am I missing?

It must be something really silly I do wrong here. useEffect() works perfectly with MonthModificatorHandler but not re-render when using dayClick. When dayclick was only adding days re-render worked properly. After adding logic to remove days already in state re-rendering stopped. I can call saveChanges and loadTimeline to fix functionality but if you click few days in a row asynchronous call leads to unexpected results. Thanks for your time.
export default function DatePicker(props) {
const classes = useStyles();
const theme = useTheme();
const [monthModificator, setMonthModificator] = React.useState(0);
const [monthMatrix, setMonthMatrix] = React.useState([]);
const [selectedDates, setSelectedDates] = React.useState([]);
const MonthModificatorHandler = value => {
setMonthModificator(monthModificator + value);
};
const dayClick = day => {
let data = selectedDates;
let addDay = true;
if (data.length === 0) {
data.push(day);
} else {
data.map((date, index) => {
if (day.equals(date)) {
data.splice(index, 1);
addDay = false;
}
});
if (addDay) {
data.push(day);
}
}
setSelectedDates(data);
// saveChanges();
// loadTimeline();
};
let now = DateTime.local().plus({ months: monthModificator });
let firstDayOfFirstWeek = now.startOf("month").startOf("week");
let lastDayOfLasttWeek = now.endOf("month").endOf("week");
let monthToDisplay = Interval.fromDateTimes(
firstDayOfFirstWeek,
lastDayOfLasttWeek
);
function loadTimeline() {
axios.get(`/timeline`).then(response => {
let selectedDays = [];
response.data.map(date => {
selectedDays.push(DateTime.fromISO(date));
});
setSelectedDates(selectedDays);
});
}
useEffect(() => {
let load = true;
if (load) {
loadTimeline();
load = false;
}
var matrix = [];
for (let v = 0; v < monthToDisplay.length("day"); v++) {
matrix.push(firstDayOfFirstWeek.plus({ day: v }));
}
setMonthMatrix(matrix);
}, [selectedDates, monthModificator]);
function saveChanges() {
let arrayOfDataObjects = selectedDates;
let arrayOfDataStrings = arrayOfDataObjects.map(singleDataObject => {
return (
"," +
JSON.stringify(singleDataObject.toISODate()).replaceAll('"', "") // extra quotes removed
);
});
axios.post(`/timeline`, {
timeline: arrayOfDataStrings
});
}
return (
<Grid container justify="space-around">
<Button onClick={() => MonthModificatorHandler(1)}>+</Button>
<Button onClick={() => MonthModificatorHandler(-1)}>-</Button>
<Card className={classes.root}>
{monthMatrix.map((day, index) => {
let color = "secondary";
selectedDates.map(workingDay => {
if (day.equals(workingDay)) {
color = "primary";
}
});
return (
<Button
color={color}
variant="contained"
onClick={() => dayClick(day)}
className={classes.days}
key={index}
>
{day.day}
</Button>
);
})}
</Card>
<Button onClick={() => saveChanges()}>Save Changes</Button>
<Button onClick={() => loadTimeline()}>Update Changes</Button>
</Grid>
);
}
Maybe the problem is that you compute new state from previous state. It should be done with callback https://reactjs.org/docs/hooks-reference.html#functional-updates
Try something like
const dayClick = day => setSelectedDates((_data) => {
let data =[..._data];
let addDay = true;
if (data.length === 0) {
data.push(day);
} else {
data.map((date, index) => {
if (day.equals(date)) {
data.splice(index, 1);
addDay = false;
}
});
if (addDay) {
data.push(day);
}
}
return data
})
Answered by Kostya Tresko, thank you. On top of that, another mistake was in the hook itself. The way I loaded data caused re rending loop.
if (load) {
loadTimeline();
load = false;
}
DO NOT DO THAT

Resources