reactjs - 当 useState 钩子更改状态时,React.js 组件不会重新渲染子组件
问题描述
我有一个父组件,NavBar
它有两个孩子:NavItems
和Icon
。当其中任何一个孩子被点击时,他们的外观都应该改变。一个非常标准的功能。
另外我想要的是能够根据屏幕宽度设置初始状态。为此,我创建了一个自定义挂钩,我称之为useMatchMedia
. 每次useMatchMedia
调用时,我都需要覆盖该状态,而不管它当前具有什么状态以及使用这个新状态重新渲染子组件。
我相信问题NavBar
出在 return 声明之前。但以防万一,我也为我的自定义钩子提供了代码。
(滚动到我的代码下方,查看我解决问题的尝试)。
这是父母:
function NavBar() {
const matchMedia = useMatchMedia(false, "tablet"); // returns true or false if the screen width is that of a tablet or not
const [hideNavItems, setHideNavItems] = useState(matchMedia);
let onHandleHideNavItems = () => {
setHideNavItems(!hideNavItems);
};
return (
<Nav>
<NavItems
hideNavItems={hideNavItems}
onHandleHideNavItems={onHandleHideNavItems} // onClick={() => onHandleHideNavItems(!hideNavItems)}
/>
<Icon
hideNavItems={hideNavItems}
onHandleHideNavItems={onHandleHideNavItems} // onClick={() => onHandleHideNavItems(!hideNavItems)}
/>
</Nav>
);
}
export default NavBar;
这是我的自定义钩子:
function useMatchMedia(initialState, device) {
const [passQuery, setPassQuery] = useState(initialState);
const matchMediaRef = useRef(null);
let widthType = null;
let chosenDevice = device;
switch (chosenDevice) {
case "phone":
chosenDevice = 425;
widthType = "max";
break;
case "tablet":
chosenDevice = 768;
widthType = "max";
break;
case "smallLaptop":
chosenDevice = 1024;
widthType = "max";
break;
case "largeLaptop":
chosenDevice = 1440;
widthType = "max";
break;
default:
chosenDevice = 1800;
widthType = "min";
}
useEffect(() => {
matchMediaRef.current = window.matchMedia(
`(${widthType}-width: ${chosenDevice}px)`
);
const initialMatch = matchMediaRef.current.matches;
if (initialMatch) {
setPassQuery(true);
} else {
setPassQuery(false);
}
const target = (event) => {
if (event.matches) {
setPassQuery(true);
} else {
setPassQuery(false);
}
};
matchMediaRef.current.addListener(target);
return () => {
matchMediaRef.current.removeListener(target);
};
}, [widthType, chosenDevice]);
return passQuery;
}
export default useMatchMedia;
我尝试useEffect
在我的 NavBar 组件中使用,例如:
useEffect(() => {
setHideNavItems(!hideNavItems);
}, [matchMedia, hideNavItems]);
这样,每当 matchMedia 发生变化时,我都应该重新渲染,并且初始状态hideNavItems
设置为matchMedia
应该更新我的状态,从而更新孩子。这失败了,因为它创建了一个无限循环。我理解为什么会有一个无限循环,但我也相信解决方案接近这样的东西。
我也尝试过使用useCallback
ononHandleHideNavItems
但这不起作用。
我尝试使用useRef
将渲染限制为更新而不是初始渲染。这也行不通。
我有点失落。任何帮助深表感谢。
解决方案
推荐阅读
- slick.js - Slick Carousel淡入淡出设置仅显示第一张幻灯片
- python - 可以取消的异步键盘输入
- c# - 图像未在 MVC 中上传
- networking - 通过 ActiveMQ / AMQ 包装 http / gRPC 流量
- apache-spark - 尝试将数据从 Ignite 加载到 Spark 数据帧时出错
- javascript - 在没有 https 的情况下使用加密库时有哪些安全威胁?
- c++ - 漏洞?当数组具有模板大小时,为什么我不能在数组成员上使用模板函数
- css - 暗模式下嵌入 SVG 图像的问题
- r - 如何在 R 中使用 ggplot2 在箱线图的每个四分位数中包含观察数?
- python - TensorFlow:来自分布的随机正态值,参数=张量