正在重置状态
State being reset
React 和 React hooks 新手。我试图理解为什么我的状态被 initialState 覆盖。
当我输入一个值时,一切正常。但是,当我调整 window 大小时,它会将其重置为 initialState,我不确定为什么。
将值插入输入后 updateBox
显示正确的值。
谁能帮我解释一下这是怎么回事?
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;
尝试将您的初始状态移到组件之外或直接设置它。在您的调整大小函数中,您正在设置另一个状态对象,它是 re-rendering 组件,但由于您的初始状态在组件内部,我认为您只是将它添加回每个 re-render.[= 的状态11=]
也可能与您的问题无关,您可能需要清理调整大小功能,否则您很可能会遇到一些问题。综上所述,请尝试执行以下操作:
import React,{useState,useEffect,useRef} from 'react';
import './VenueLayout.scss';
import {clone} from '../../../common/utils/clone'
const initVenueLayout = {
venueSize:{
width: 16,
height: 16,
}
}
const VenueLayout = ({props}) => {
const boxContainer = useRef(null);
const [venueLayoutState,setVenueLayoutState] = useState(initVenueLayout)
const [boxStyle,setBoxStyle] = useState({
"height": "100%",
"width": "90%",
})
useEffect(() =>{
// State did update
});
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("venueLayoutState did update",venueLayoutState)
updateBox()
}, [venueLayoutState]);
const updateBox = () => {
console.log("updateBox",venueLayoutState.venueSize.width,":",venueLayoutState.venueSize.height)
let percentage = (venueLayoutState.venueSize.height/venueLayoutState.venueSize.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 venueSizeRatioOnChange = (widthArg,heightArg) => {
let width = (widthArg === null) ? venueLayoutState.venueSize.width : widthArg;
let height = (heightArg === null) ? venueLayoutState.venueSize.height : heightArg;
const cloneVenueLayoutState = clone(venueLayoutState);
cloneVenueLayoutState.venueSize.width = width
cloneVenueLayoutState.venueSize.height = height
setVenueLayoutState(cloneVenueLayoutState)
}
const render = () => {
console.log("render",venueLayoutState.venueSize.width,":",venueLayoutState.venueSize.height)
// updateBox()
const enlargeClass = (props.enlarge) ? " enlarge" : "" ;
return (
<div className={"venue-layout"+enlargeClass} >
<div className="venue-layout-tools-top">
Venue Size Ratio
<input
type="number"
value={venueLayoutState.venueSize.width}
onChange={(e) => {venueSizeRatioOnChange(e.target.value,null)}}/>
by
<input
type="number"
value={venueLayoutState.venueSize.height}
onChange={(e) => {venueSizeRatioOnChange(null,e.target.value)}}/>
<button className="button close" onClick={props.toggleVenueLayout}>
Close
</button>
</div>
<div className="venue-layout-container">
<div className="venue-layout-tools-side">
side tools
</div>
<div className="venue-layout-box-container" ref={boxContainer}>
<div className="venue-layout-box" style={boxStyle}>
{boxStyle.height} x {boxStyle.width}
</div>
</div>
</div>
</div>
);
}
return render();
};
export default VenueLayout;
问题出在这里:
useEffect(() => {
// Component did mount
console.log("Component did mount")
window.addEventListener("resize", updateBox); //HERE
return () => {
window.removeEventListener("resize", updateBox);
};
}, []);
您绑定到调整大小 window 事件的 updateBox 函数将使用它在绑定时的状态(当组件安装时),因为addEventListener 的回调在它自己的闭包中执行。
为了解决这个问题,你必须将状态保存在一个 ref 中,这样它仍然是当前的,当你从另一个闭包访问它时也是如此。
所以首先创建 ref 并将状态存储在其中:
const [layoutState,setLayoutState] = useState(initLayout);
const layoutRef= useRef({});
layoutRef.current = layoutState;
然后在 updateBox 函数中,您必须从 layoutRef 而不是 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)
}
让我知道它是否有效,请提供反馈,因为这是我在这里的第一个答案:)
React 和 React hooks 新手。我试图理解为什么我的状态被 initialState 覆盖。
当我输入一个值时,一切正常。但是,当我调整 window 大小时,它会将其重置为 initialState,我不确定为什么。
将值插入输入后 updateBox
显示正确的值。
谁能帮我解释一下这是怎么回事?
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;
尝试将您的初始状态移到组件之外或直接设置它。在您的调整大小函数中,您正在设置另一个状态对象,它是 re-rendering 组件,但由于您的初始状态在组件内部,我认为您只是将它添加回每个 re-render.[= 的状态11=]
也可能与您的问题无关,您可能需要清理调整大小功能,否则您很可能会遇到一些问题。综上所述,请尝试执行以下操作:
import React,{useState,useEffect,useRef} from 'react';
import './VenueLayout.scss';
import {clone} from '../../../common/utils/clone'
const initVenueLayout = {
venueSize:{
width: 16,
height: 16,
}
}
const VenueLayout = ({props}) => {
const boxContainer = useRef(null);
const [venueLayoutState,setVenueLayoutState] = useState(initVenueLayout)
const [boxStyle,setBoxStyle] = useState({
"height": "100%",
"width": "90%",
})
useEffect(() =>{
// State did update
});
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("venueLayoutState did update",venueLayoutState)
updateBox()
}, [venueLayoutState]);
const updateBox = () => {
console.log("updateBox",venueLayoutState.venueSize.width,":",venueLayoutState.venueSize.height)
let percentage = (venueLayoutState.venueSize.height/venueLayoutState.venueSize.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 venueSizeRatioOnChange = (widthArg,heightArg) => {
let width = (widthArg === null) ? venueLayoutState.venueSize.width : widthArg;
let height = (heightArg === null) ? venueLayoutState.venueSize.height : heightArg;
const cloneVenueLayoutState = clone(venueLayoutState);
cloneVenueLayoutState.venueSize.width = width
cloneVenueLayoutState.venueSize.height = height
setVenueLayoutState(cloneVenueLayoutState)
}
const render = () => {
console.log("render",venueLayoutState.venueSize.width,":",venueLayoutState.venueSize.height)
// updateBox()
const enlargeClass = (props.enlarge) ? " enlarge" : "" ;
return (
<div className={"venue-layout"+enlargeClass} >
<div className="venue-layout-tools-top">
Venue Size Ratio
<input
type="number"
value={venueLayoutState.venueSize.width}
onChange={(e) => {venueSizeRatioOnChange(e.target.value,null)}}/>
by
<input
type="number"
value={venueLayoutState.venueSize.height}
onChange={(e) => {venueSizeRatioOnChange(null,e.target.value)}}/>
<button className="button close" onClick={props.toggleVenueLayout}>
Close
</button>
</div>
<div className="venue-layout-container">
<div className="venue-layout-tools-side">
side tools
</div>
<div className="venue-layout-box-container" ref={boxContainer}>
<div className="venue-layout-box" style={boxStyle}>
{boxStyle.height} x {boxStyle.width}
</div>
</div>
</div>
</div>
);
}
return render();
};
export default VenueLayout;
问题出在这里:
useEffect(() => {
// Component did mount
console.log("Component did mount")
window.addEventListener("resize", updateBox); //HERE
return () => {
window.removeEventListener("resize", updateBox);
};
}, []);
您绑定到调整大小 window 事件的 updateBox 函数将使用它在绑定时的状态(当组件安装时),因为addEventListener 的回调在它自己的闭包中执行。
为了解决这个问题,你必须将状态保存在一个 ref 中,这样它仍然是当前的,当你从另一个闭包访问它时也是如此。 所以首先创建 ref 并将状态存储在其中:
const [layoutState,setLayoutState] = useState(initLayout);
const layoutRef= useRef({});
layoutRef.current = layoutState;
然后在 updateBox 函数中,您必须从 layoutRef 而不是 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)
}
让我知道它是否有效,请提供反馈,因为这是我在这里的第一个答案:)