为什么在使用 React Hooks 时我的尺寸没有在调整大小时更新
Why are my dimensions not updating on resize when using React Hooks
每当 window 调整大小时,我的尺寸都不会更新。在下面的代码中,您可以看到 window.innerHeight 已更新,但尺寸没有。我可能遗漏了一些东西,但我还没有弄明白。
// Navbar.components.ts:
export const sidebar = () => {
let height;
let width;
if (typeof window !== `undefined`) {
height = window.innerHeight
width = window.innerWidth
}
const [dimensions, setDimensions] = useState({
windowHeight: height,
windowWidth: width,
})
useEffect(() => {
const debouncedHandleResize = debounce(function handleResize() {
setDimensions({
windowHeight: window.innerHeight,
windowWidth: window.innerWidth,
});
// Logging window.innerHeight gives the current height,
// Logging dimensions.windowHeight gives the initial height
console.log(window.innerHeight, " . ", dimensions.windowHeight)
}, 100);
window.addEventListener(`resize`, debouncedHandleResize)
return () => window.removeEventListener('resize', debouncedHandleResize)
}, [])
return {
open: () => ({
clipPath: `circle(${dimensions.windowHeight * 2 + 200}px at 40px 40px)`,
transition: {
type: "spring",
stiffness: 20,
restDelta: 2
}
}),
closed: () => ({
clipPath: `circle(30px at ${300 - 40}px ${dimensions.windowHeight - 45}px)`,
transition: {
delay: 0.2,
type: "spring",
stiffness: 400,
damping: 40
}
})
}
}
我这样使用侧边栏:
// Navbar.tsx
const Navbar: React.FC<NavbarProps> = () => {
...
return {
...
<MobileNavBackground variants={sidebar()} />
...
}
}
这是调整 window 大小时返回的日志示例:
更新 1
@sygmus1897
代码更改为:
// Navbar.tsx:
const Navbar: React.FC<NavbarProps> = () => {
const [windowWidth, windowHeight] = getDimensions();
useEffect(() => {
}, [windowWidth, windowHeight])
return (
...
<MobileNavWrapper
initial={false}
animate={menuIsOpen ? "open" : "closed"}
custom={height}
ref={ref}
menuIsOpen={menuIsOpen}
>
<MobileNavBackground variants={sidebar} custom={windowHeight} />
<MobileNav menuIsOpen={menuIsOpen} toggleMenu={toggleMenu} />
<MenuToggle toggle={() => toggleMenu()} />
</MobileNavWrapper>
)
}
// getDimensions()
export const getDimensions = () => {
const [dimension, setDimension] = useState([window.innerWidth, window.innerHeight]);
useEffect(() => {
window.addEventListener("resize", () => {
setDimension([window.innerWidth, window.innerHeight])
});
return () => {
window.removeEventListener("resize", () => {
setDimension([window.innerWidth, window.innerHeight])
})
}
}, []);
return dimension;
};
// Navbar.components.ts
export const sidebar = {
open: (height) => ({
clipPath: `circle(${height + 200}px at 40px 40px)`,
transition: {
type: "spring",
stiffness: 20,
restDelta: 2
}
}),
closed: (height) => ({
clipPath: `circle(30px at ${300 - 60}px ${height - 65}px)`,
transition: {
delay: 0.2,
type: "spring",
stiffness: 400,
damping: 40
}
})
}
问题仍然存在,调整 window 的大小不会影响圆的 clipPath 位置。为了直观地说明问题,汉堡包应该在绿色圆圈内:
您可以创建自定义挂钩来监听 window 调整大小。
您可以根据您的要求修改此 link 的解决方案
通过使用 useState 而不是 ref,在调整大小时更新它并将值返回到您的主要组件
这是一个例子:
export default function useWindowResize() {
const [dimension, setDimension] = useState([0, 0]);
useEffect(() => {
window.addEventListener("resize", () => {
setDimension([window.innerWidth, window.innerHeight])
});
return () => {
window.removeEventListener("resize", () => {
setDimension([window.innerWidth, window.innerHeight])
})
}
}, []);
return dimension;
}
在你的主要组件中使用它:
const MainComponent = () => {
const [width, height] = useWindowResize();
useEffect(()=>{
// your operations
}, [width, height])
}
每次更改尺寸时,您的组件都会更新。你会得到更新后的宽度和高度
编辑:
Framer-motion 提供了一种动态设置变量属性的方法(详细指南请参考Dynamically Update Variant):-
// Navbar.tsx:
const Navbar: React.FC<NavbarProps> = () => {
return (
...
<MobileNavWrapper
initial={false}
custom={window.innerWidth} // custom={window.innerHeight} if variable depends on Height
animate={menuIsOpen ? "open" : "closed"}
custom={height}
ref={ref}
menuIsOpen={menuIsOpen}
>
<MobileNavBackground variants={sidebar} />
<MobileNav menuIsOpen={menuIsOpen} toggleMenu={toggleMenu} />
<MenuToggle toggle={() => toggleMenu()} />
</MobileNavWrapper>
)
}
// Navbar.components.ts
export const sidebar = {
open: (width) => ({
clipPath: `circle(${width+ 200}px at 40px 40px)`,
transition: {
type: "spring",
stiffness: 20,
restDelta: 2
}
}),
closed: (width) => ({
clipPath: `circle(30px at ${300 - 60}px ${width- 65}px)`,
transition: {
delay: 0.2,
type: "spring",
stiffness: 400,
damping: 40
}
})
}
感谢这个帖子: 我发现我遇到的问题是 framer-motion 中的错误。要解决此问题,请向重新渲染出现问题的运动组件添加一个键值。这确保 React 重新渲染组件。
就我而言,我所要做的就是:
<MobileNavBackground variants={sidebar} custom={windowHeight} key={key} />
每当 window 调整大小时,我的尺寸都不会更新。在下面的代码中,您可以看到 window.innerHeight 已更新,但尺寸没有。我可能遗漏了一些东西,但我还没有弄明白。
// Navbar.components.ts:
export const sidebar = () => {
let height;
let width;
if (typeof window !== `undefined`) {
height = window.innerHeight
width = window.innerWidth
}
const [dimensions, setDimensions] = useState({
windowHeight: height,
windowWidth: width,
})
useEffect(() => {
const debouncedHandleResize = debounce(function handleResize() {
setDimensions({
windowHeight: window.innerHeight,
windowWidth: window.innerWidth,
});
// Logging window.innerHeight gives the current height,
// Logging dimensions.windowHeight gives the initial height
console.log(window.innerHeight, " . ", dimensions.windowHeight)
}, 100);
window.addEventListener(`resize`, debouncedHandleResize)
return () => window.removeEventListener('resize', debouncedHandleResize)
}, [])
return {
open: () => ({
clipPath: `circle(${dimensions.windowHeight * 2 + 200}px at 40px 40px)`,
transition: {
type: "spring",
stiffness: 20,
restDelta: 2
}
}),
closed: () => ({
clipPath: `circle(30px at ${300 - 40}px ${dimensions.windowHeight - 45}px)`,
transition: {
delay: 0.2,
type: "spring",
stiffness: 400,
damping: 40
}
})
}
}
我这样使用侧边栏:
// Navbar.tsx
const Navbar: React.FC<NavbarProps> = () => {
...
return {
...
<MobileNavBackground variants={sidebar()} />
...
}
}
这是调整 window 大小时返回的日志示例:
更新 1
@sygmus1897
代码更改为:
// Navbar.tsx:
const Navbar: React.FC<NavbarProps> = () => {
const [windowWidth, windowHeight] = getDimensions();
useEffect(() => {
}, [windowWidth, windowHeight])
return (
...
<MobileNavWrapper
initial={false}
animate={menuIsOpen ? "open" : "closed"}
custom={height}
ref={ref}
menuIsOpen={menuIsOpen}
>
<MobileNavBackground variants={sidebar} custom={windowHeight} />
<MobileNav menuIsOpen={menuIsOpen} toggleMenu={toggleMenu} />
<MenuToggle toggle={() => toggleMenu()} />
</MobileNavWrapper>
)
}
// getDimensions()
export const getDimensions = () => {
const [dimension, setDimension] = useState([window.innerWidth, window.innerHeight]);
useEffect(() => {
window.addEventListener("resize", () => {
setDimension([window.innerWidth, window.innerHeight])
});
return () => {
window.removeEventListener("resize", () => {
setDimension([window.innerWidth, window.innerHeight])
})
}
}, []);
return dimension;
};
// Navbar.components.ts
export const sidebar = {
open: (height) => ({
clipPath: `circle(${height + 200}px at 40px 40px)`,
transition: {
type: "spring",
stiffness: 20,
restDelta: 2
}
}),
closed: (height) => ({
clipPath: `circle(30px at ${300 - 60}px ${height - 65}px)`,
transition: {
delay: 0.2,
type: "spring",
stiffness: 400,
damping: 40
}
})
}
问题仍然存在,调整 window 的大小不会影响圆的 clipPath 位置。为了直观地说明问题,汉堡包应该在绿色圆圈内:
您可以创建自定义挂钩来监听 window 调整大小。
您可以根据您的要求修改此 link 的解决方案
通过使用 useState 而不是 ref,在调整大小时更新它并将值返回到您的主要组件
这是一个例子:
export default function useWindowResize() {
const [dimension, setDimension] = useState([0, 0]);
useEffect(() => {
window.addEventListener("resize", () => {
setDimension([window.innerWidth, window.innerHeight])
});
return () => {
window.removeEventListener("resize", () => {
setDimension([window.innerWidth, window.innerHeight])
})
}
}, []);
return dimension;
}
在你的主要组件中使用它:
const MainComponent = () => {
const [width, height] = useWindowResize();
useEffect(()=>{
// your operations
}, [width, height])
}
每次更改尺寸时,您的组件都会更新。你会得到更新后的宽度和高度
编辑:
Framer-motion 提供了一种动态设置变量属性的方法(详细指南请参考Dynamically Update Variant):-
// Navbar.tsx:
const Navbar: React.FC<NavbarProps> = () => {
return (
...
<MobileNavWrapper
initial={false}
custom={window.innerWidth} // custom={window.innerHeight} if variable depends on Height
animate={menuIsOpen ? "open" : "closed"}
custom={height}
ref={ref}
menuIsOpen={menuIsOpen}
>
<MobileNavBackground variants={sidebar} />
<MobileNav menuIsOpen={menuIsOpen} toggleMenu={toggleMenu} />
<MenuToggle toggle={() => toggleMenu()} />
</MobileNavWrapper>
)
}
// Navbar.components.ts
export const sidebar = {
open: (width) => ({
clipPath: `circle(${width+ 200}px at 40px 40px)`,
transition: {
type: "spring",
stiffness: 20,
restDelta: 2
}
}),
closed: (width) => ({
clipPath: `circle(30px at ${300 - 60}px ${width- 65}px)`,
transition: {
delay: 0.2,
type: "spring",
stiffness: 400,
damping: 40
}
})
}
感谢这个帖子:
就我而言,我所要做的就是:
<MobileNavBackground variants={sidebar} custom={windowHeight} key={key} />