【发布时间】:2021-03-05 10:13:32
【问题描述】:
我有一个父组件NavBar,它有两个子组件:NavItems 和Icon。当其中任何一个孩子被点击时,他们的外观都应该改变。一个非常标准的功能。
另外我想要的是能够根据屏幕宽度设置初始状态。为此,我创建了一个自定义挂钩,我称之为useMatchMedia。每次调用 useMatchMedia 时,我都需要覆盖该状态,无论它当前具有什么状态以及使用这个新状态重新渲染子组件。
我相信问题出在 return 语句之前的 NavBar 内。但以防万一,我也提供了自定义挂钩的代码。
(滚动到我的代码下方,查看我解决问题的尝试)。
这是父级:
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;
我曾尝试在我的 NavBar 组件中使用useEffect,例如:
useEffect(() => {
setHideNavItems(!hideNavItems);
}, [matchMedia, hideNavItems]);
这样,每当 matchMedia 发生变化时,我都应该重新渲染,因为 hideNavItems 的初始状态设置为 matchMedia,这应该会更新我的状态,从而更新孩子。这失败了,因为它创建了一个无限循环。我理解为什么会有一个无限循环,但我也相信解决方案接近于这样。
我也尝试在onHandleHideNavItems 上使用useCallback,但这不起作用。
我尝试使用useRef 将渲染限制为更新而不是初始渲染。这也不起作用。
我有点迷茫。非常感谢任何帮助。
【问题讨论】:
-
抱歉,我对您要完成的工作有点困惑,请查看此sandbox 这是您需要的吗?
-
我不敢相信我离我有多近却忽略了这一点。如果您发布您的“useEffect”,我会将其标记为已接受的答案。谢谢。
-
是的,确实非常接近。我很高兴我的云帮助。谢谢。
-
我发现你的回答有问题。我需要状态被“UseMatchMedia”覆盖。如果屏幕尺寸变大时“hideNavItems”已经为假,这意味着“UseMatchMedia”也将返回假。 “NavItems”组件不会重新渲染。有任何想法吗?每次调用“useMatchMedia”时,我都需要“NavItems”重新渲染。
标签: reactjs react-hooks