Newbie to React & React hooks. I'm trying to understand why my state is being overwritten by initialState.
When I input a value, things work as expected. However when I resize the window it resets it to the initialState and I'm not sure why.
After inserting values into the inputs updateBox
displays the correct values.
Can anybody break this down for me as to what is happening?
https://stackblitz.com/edit/react-hdf3sg
import React,{useState,useEffect,useRef} from 'react';
import './Layout.scss';
const initLayout = {
size:{
width: 16,
height: 16,
}
}
const clone = (obj) => {
return JSON.parse(JSON.stringify(obj));
}
const Layout = ({props}) => {
const boxContainer = useRef(null);
const [layoutState,setLayoutState] = useState(initLayout)
const [boxStyle,setBoxStyle] = useState({
"height": "100%",
"width": "90%",
})
useEffect(() =>{
// State did update
console.log("useEffect:State did update:",layoutState.size.width,":",layoutState.size.height)
// updateBox()
});
useEffect(() =>{
// Component did mount
console.log("Component did mount")
window.addEventListener("resize", updateBox);
return () => {
window.removeEventListener("resize", updateBox);
};
}, []);
useEffect(() => {
// This state did update
console.log("layoutState did update",layoutState)
updateBox()
}, [layoutState]);
const updateBox = () => {
console.log("updateBox",layoutState.size.width,":",layoutState.size.height)
let percentage = (layoutState.size.height/layoutState.size.width) * 100
if(percentage > 100){
percentage = 100
}
percentage = percentage+"%"
// console.log("percentage",percentage)
const cloneBoxStyle = clone(boxStyle)
cloneBoxStyle.width = "100%"
cloneBoxStyle.height = percentage
setBoxStyle(cloneBoxStyle)
// console.log("boxContainer",boxContainer)
// console.log("height",boxContainer.current.clientHeight)
// console.log("width",boxContainer.current.clientWidth)
}
const sizeRatioOnChange = (widthArg,heightArg) => {
let width = (widthArg === null) ? layoutState.size.width : widthArg;
let height = (heightArg === null) ? layoutState.size.height : heightArg;
const cloneLayoutState = clone(layoutState);
cloneLayoutState.size.width = width
cloneLayoutState.size.height = height
setLayoutState(cloneLayoutState)
}
const render = () => {
console.log("render",layoutState.size.width,":",layoutState.size.height)
// updateBox()
// const enlargeClass = (true) ? " enlarge" : "" ;
return (
<div className={"layout"} >
<div className="layout-tools-top">
Layout Size Ratio
<input
type="number"
value={layoutState.size.width}
onChange={(e) => {sizeRatioOnChange(e.target.value,null)}}/>
by
<input
type="number"
value={layoutState.size.height}
onChange={(e) => {sizeRatioOnChange(null,e.target.value)}}/>
<button className="button close">
Close
</button>
</div>
<div className="layout-container">
<div className="layout-tools-side">
<h2>Header</h2>
<ul>
</ul>
<form>
<input type="text" placeholder="Add Item" />
</form>
</div>
<div className="layout-box-container" ref={boxContainer}>
<div className="layout-box" style={boxStyle}>
{/* {boxStyle.height} x {boxStyle.width} */}
</div>
</div>
</div>
</div>
);
}
return render();
};
export default Layout;
In a computer or data transmission system, a reset clears any pending errors or events and brings a system to normal condition or an initial state, usually in a controlled manner.
What setState does? setState() enqueues changes to the component state and tells React that this component and its children need to be re-rendered with the updated state. This is the primary method you use to update the user interface in response to event handlers and server responses.
When you directly update the state, it does not change this. state immediately. Instead, it creates a pending state transition, and accessing it after calling this method will only return the present value. You will lose control of the state across all components.
To reset a component to its initial state:Store the initial state in a variable. When an event occurs, call the setState() function, passing it the initial state.
The problem is here:
useEffect(() => {
// Component did mount
console.log("Component did mount")
window.addEventListener("resize", updateBox); //HERE
return () => {
window.removeEventListener("resize", updateBox);
};
}, []);
the updateBox function you are binding to the resize window event will use the state it had at the moment it was bounded (when the component did mount) because the callback of addEventListener is executed in its own closure.
To solve this you have to save the state inside a ref, that way it will still be current, also when you are accessing it from another closure. so first create the ref and store the state in it:
const [layoutState,setLayoutState] = useState(initLayout);
const layoutRef= useRef({});
layoutRef.current = layoutState;
and then inside the updateBox function you have to read the layout "state" from layoutRef instead of layoutState:
const updateBox = () => {
let percentage = (layoutRef.current.size.height/layoutRef.current.size.width) * 100
if(percentage > 100){
percentage = 100
}
percentage = percentage+"%"
const cloneBoxStyle = clone(boxStyle);
cloneBoxStyle.width = "100%"
cloneBoxStyle.height = percentage
setBoxStyle(cloneBoxStyle)
}
Let me know if it works and please give feedback since this is my first answer here :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With