如何从传递给侦听器的回调中访问状态挂钩值?
How can I access a state hook value from a callback passed to a listener?
我有一个组件需要监听 window 调整大小和 hide/show 侧边栏。
我决定为此目的创建一个自定义挂钩,但它似乎无法正常工作。
主要问题是 mainSidebar
值始终不变,并且未正确读取更新。
这是挂钩本身:
export function useResizeListener(cb: (this: Window, e: UIEvent) => any) {
const [size, setSize] = React.useState({ width: window.outerWidth, height: window.outerHeight })
React.useEffect(() => {
function _wrapped(this: Window, e: UIEvent): any {
console.debug('setting size and calling callback', { width: this.outerWidth, height: this.outerHeight })
setSize({ width: this.outerWidth, height: this.outerHeight })
cb.call(this, e)
}
window.addEventListener('resize', _wrapped)
return () => window.removeEventListener('resize', _wrapped)
}, [size.width, size.height])
}
这是使用它的组件:
const shouldHaveSidebar = () => document.body.clientWidth >= ScreenSizes.Tablet
const HomePage: React.FunctionComponent<IProps> = (props) => {
const [mainSidebar, toggleMainSidebar] = React.useState(shouldHaveSidebar())
useResizeListener(() => {
console.debug('device width vs tablet width:', document.body.clientWidth, '>=', ScreenSizes.Tablet)
console.debug('shouldHaveSidebar vs mainSidebar', shouldHaveSidebar(), '!==', mainSidebar)
if (shouldHaveSidebar() !== mainSidebar) {
console.debug('setting to', shouldHaveSidebar())
toggleMainSidebar(shouldHaveSidebar())
}
})
return ...
}
在 Chrome dev inspector 设备模式下切换视口大小后,我在控制台中得到了这个输出。注意我从桌面开始,然后改为移动 L.
setting size and calling callback {width: 1680, height: 1027}
device width vs tablet width: 1146 >= 800
shouldHaveSidebar vs mainSidebar true !== false
setting to true
setting size and calling callback {width: 425, height: 779}
device width vs tablet width: 425 >= 800
shouldHaveSidebar vs mainSidebar false !== false // <--- should be: false !== true
- 我尝试在
useEffect
钩子 的外部和内部之间移动 _wrapper
函数
- 我尝试将回调包装在 setTimeout/setImmediate
中
- 我尝试用一个函数包装状态 get (mainSidebar bool) 以检索它,该函数在组件中和钩子调用之外定义
这些都不起作用。
我做错了什么?
我建议重组解决方案。首先,我会将 useResizeListener
更改为一个钩子,它为您提供 window 尺寸:
export function useWindowSize() {
const [size, setSize] = React.useState({ width: window.clientWidth, height: window.clientHeight });
React.useEffect(() => {
function handleResize(e: UIEvent) {
setSize({ width: window.clientWidth, height: window.clientHeight });
}
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return size;
}
然后在您的 HomePage
组件中:
const shouldHaveSidebar = (size) => size.width >= ScreenSizes.Tablet
const HomePage: React.FunctionComponent<IProps> = (props) => {
const size = useWindowSize();
const mainSidebar = shouldHaveSidebar(size);
// Now if you want to perform an action when the size changes you cna simply do:
useEffect(() => {
// Perform your action here
}, [size.width, size.height]);
// ...
}
我有一个组件需要监听 window 调整大小和 hide/show 侧边栏。 我决定为此目的创建一个自定义挂钩,但它似乎无法正常工作。
主要问题是 mainSidebar
值始终不变,并且未正确读取更新。
这是挂钩本身:
export function useResizeListener(cb: (this: Window, e: UIEvent) => any) {
const [size, setSize] = React.useState({ width: window.outerWidth, height: window.outerHeight })
React.useEffect(() => {
function _wrapped(this: Window, e: UIEvent): any {
console.debug('setting size and calling callback', { width: this.outerWidth, height: this.outerHeight })
setSize({ width: this.outerWidth, height: this.outerHeight })
cb.call(this, e)
}
window.addEventListener('resize', _wrapped)
return () => window.removeEventListener('resize', _wrapped)
}, [size.width, size.height])
}
这是使用它的组件:
const shouldHaveSidebar = () => document.body.clientWidth >= ScreenSizes.Tablet
const HomePage: React.FunctionComponent<IProps> = (props) => {
const [mainSidebar, toggleMainSidebar] = React.useState(shouldHaveSidebar())
useResizeListener(() => {
console.debug('device width vs tablet width:', document.body.clientWidth, '>=', ScreenSizes.Tablet)
console.debug('shouldHaveSidebar vs mainSidebar', shouldHaveSidebar(), '!==', mainSidebar)
if (shouldHaveSidebar() !== mainSidebar) {
console.debug('setting to', shouldHaveSidebar())
toggleMainSidebar(shouldHaveSidebar())
}
})
return ...
}
在 Chrome dev inspector 设备模式下切换视口大小后,我在控制台中得到了这个输出。注意我从桌面开始,然后改为移动 L.
setting size and calling callback {width: 1680, height: 1027}
device width vs tablet width: 1146 >= 800
shouldHaveSidebar vs mainSidebar true !== false
setting to true
setting size and calling callback {width: 425, height: 779}
device width vs tablet width: 425 >= 800
shouldHaveSidebar vs mainSidebar false !== false // <--- should be: false !== true
- 我尝试在
useEffect
钩子 的外部和内部之间移动 - 我尝试将回调包装在 setTimeout/setImmediate 中
- 我尝试用一个函数包装状态 get (mainSidebar bool) 以检索它,该函数在组件中和钩子调用之外定义
_wrapper
函数
这些都不起作用。
我做错了什么?
我建议重组解决方案。首先,我会将 useResizeListener
更改为一个钩子,它为您提供 window 尺寸:
export function useWindowSize() {
const [size, setSize] = React.useState({ width: window.clientWidth, height: window.clientHeight });
React.useEffect(() => {
function handleResize(e: UIEvent) {
setSize({ width: window.clientWidth, height: window.clientHeight });
}
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return size;
}
然后在您的 HomePage
组件中:
const shouldHaveSidebar = (size) => size.width >= ScreenSizes.Tablet
const HomePage: React.FunctionComponent<IProps> = (props) => {
const size = useWindowSize();
const mainSidebar = shouldHaveSidebar(size);
// Now if you want to perform an action when the size changes you cna simply do:
useEffect(() => {
// Perform your action here
}, [size.width, size.height]);
// ...
}