So I have a website with a global state that I created using redux. This holds a list of every window there is. I have seperate state for the windows itself to track it’s position and size on the screen. I used an effect to update the global state when the local state changes. However, when the state is changed, the entire website reloads. This normally wouldn’t be a problem but I have a loading screen that show up and fades out everytime you move a window. Why is this happening?
/modules/Window.jsx:
import { Component, createRef, useState, createContext, useEffect } from "react";
import { createSlice, configureStore } from '@reduxjs/toolkit';
import { useSelector, useDispatch } from 'react-redux';
import { createWindow, updateWindow, destroyWindow } from "../store/windowslice";
import { Provider } from 'react-redux';
export const generateId=(length)=>{
let result='';
const characters='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const charactersLength = characters.length;
let counter=0;
while (counter<length) {
result+=characters.charAt(Math.floor(Math.random() * charactersLength));
counter+=1;
}
return btoa(result);
}
export const Window=(props)=>{
const windows=useSelector((state)=>state.windows.value);
const dispatch=useDispatch();
const[win_id,setWin_id]=useState(generateId(10));
const[state,setState]=useState({
"title": props.title,
"win_id": win_id,
"eid": props.id,
"size": props.size,
"pos": props.pos,
"includeTitlebarOptions": props.includeTitlebarOptions,
"icon": props.icon,
});
var win;
useEffect(()=>{
dispatch(updateWindow({"win_id":win_id,"windata":state}))
},[state])
const nb_actions={
close:(e)=>{
e.preventDefault();
props.callbacks.beforeWindowClose();
document.getElementById(`win_${props.id}`).remove();
dispatch(destroyWindow(win_id));
},
min:(e)=>{
e.preventDefault();
props.callbacks.beforeWindowMinimize();
const isMin=document.getElementById(`win_${props.id}_isMin?`);
document.getElementById(`win_${props.id}`).style.display="none";
isMin.setAttribute("content",!(isMin.getAttribute("content")==="true"));
},
maximize:(maxBtn,win)=>{
setState(prevState=>{
let nstate=Object.assign({},prevState);
nstate.pos={
"x": win.style.left,
"y": win.style.top};
nstate.size={
"height": win.style.height,
"width": win.style.width,};
return {nstate};});
props.callbacks.beforeWindowMaximize();
win.classList.add("maximized");
},
unmaximize:(maxBtn,win)=>{
props.callbacks.beforeWindowUnmaximize();
win.classList.remove("maximized");
win.style.top=state.pos.y;
win.style.left=state.pos.x;
win.style.height=state.size.height;
win.style.width=state.size.width;
},
maxToggle:(e)=>{
e.preventDefault();
const isMax=document.getElementById(`win_${props.id}_isMax?`);
const maxBtn=document.getElementById(`win_${props.id}_max`);
const win=document.getElementById(`win_${props.id}`);
maxBtn.innerHTML=(isMax.getAttribute("content")==="false")?"????":"????";
nb_actions[(isMax.getAttribute("content")==="false")?"maximize":"unmaximize"](maxBtn,win);
isMax.setAttribute("content",!(isMax.getAttribute("content")==="true"));
},
}
const dragElement=(elmnt)=>{
var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
setState(prevState=>{
let nstate=Object.assign({},prevState);
nstate.pos={
"x": win.style.left,
"y": win.style.top};
nstate.size={
"height": win.style.height,
"width": win.style.width,};
return {nstate};});
const dragMouseDown=(e)=>{
e = e || window.event;
e.preventDefault();
elmnt.style.transition="0s";
// get the mouse cursor position at startup:
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
// call a function whenever the cursor moves:
document.onmousemove = elementDrag;
}
const elementDrag=(e)=>{
e = e || window.event;
e.preventDefault();
// calculate the new cursor position:
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
// set the element's new position:
elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
}
const closeDragElement=()=>{
// stop moving when mouse button is released:
document.onmouseup = null;
document.onmousemove = null;
elmnt.style.transition=".5s";
setState(prevState=>{
let nstate=Object.assign({},prevState);
nstate.pos={
"x": win.style.left,
"y": win.style.top};
nstate.size={
"height": win.style.height,
"width": win.style.width,};
return {nstate};});
}
if (document.getElementById(elmnt.id + "_header")) {
// if present, the header is where you move the DIV from:
document.getElementById(elmnt.id + "_header").onmousedown = dragMouseDown;
} else {
// otherwise, move the DIV from anywhere inside the DIV:
elmnt.onmousedown = dragMouseDown;
}
}
useEffect(()=>{
const e=document.getElementById(`win_${props.id}`);
win=document.getElementById(`win_${props.id}`);
console.log(`created window with data: ${JSON.stringify(state)}`)
dragElement(e);
dispatch(createWindow({"win_id":win_id,"windata":state }))
new ResizeObserver(()=>{
e.style.transition="0s";
setState(prevState=>{
let state=Object.assign({},prevState);
state.pos={
"x": win.style.left,
"y": win.style.top};
state.size={
"height": win.style.height,
"width": win.style.width,};
return {state};});
}).observe(e);
},[])
return(<div
className="Window"
style={{
"height":props.size.height,
"width":props.size.width,
"left":props.pos.x,
"top":props.pos.y,
}}
id={`win_${props.id}`}>
<header id={`win_${props.id}_header`}>
<meta id={`win_${props.id}_isMin?`} content="false"/>
<meta id={`win_${props.id}_isMax?`} content="false"/>
<div className="icon" style={{"backgroundImage":`url("${props.icon}")`}}></div>
<h2>{props.title}</h2>
{props.includeTitlebarOptions.min?<button id={`win_${props.id}_min`} onClick={(e)=>nb_actions.min(e)}>????</button>:null}
{props.includeTitlebarOptions.max?<button id={`win_${props.id}_max`} onClick={(e)=>nb_actions.maxToggle(e)}>????</button>:null} {/* ???? for unmaximize */}
{props.includeTitlebarOptions.close?<button id={`win_${props.id}_close`} onClick={(e)=>nb_actions.close(e)}>✖</button>:null}
</header>
<div className="content">
{props.children}
</div>
</div>)
}
/store/store.jsx
import { configureStore } from '@reduxjs/toolkit'
import windowReducer from './windowslice'
export default configureStore({
reducer: {
windows:windowReducer,
},
})
/store/windowslice.jsx
import { createSlice } from '@reduxjs/toolkit'
export const windowSlice=createSlice({
name: 'windows',
initialState: {
value: {},
},
reducers: {
createWindow:(state,action)=>{
state.value=Object.assign(
{[action.payload.win_id]:action.payload.windata},state.value)},
destroyWindow:(state,action)=>{
delete state.value[action.payload];},
updateWindow:(state,action)=>{
state.value=Object.assign(
{[action.payload.win_id]:action.payload.windata},state.value)},
},
})
export const {createWindow,updateWindow,destroyWindow}=windowSlice.actions;
export default windowSlice.reducer;
If it does help, I am using vite, but it shouldn’t be doing this.