首页 > 解决方案 > 当 useState 钩子更改状态时,React.js 组件不会重新渲染子组件

问题描述

我有一个父组件,NavBar它有两个孩子:NavItemsIcon。当其中任何一个孩子被点击时,他们的外观都应该改变。一个非常标准的功能。

另外我想要的是能够根据屏幕宽度设置初始状态。为此,我创建了一个自定义挂钩,我称之为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应该更新我的状态,从而更新孩子。这失败了,因为它创建了一个无限循环。我理解为什么会有一个无限循环,但我也相信解决方案接近这样的东西。

我也尝试过使用useCallbackononHandleHideNavItems但这不起作用。

我尝试使用useRef将渲染限制为更新而不是初始渲染。这也行不通。

我有点失落。任何帮助深表感谢。

标签: reactjsreact-hooks

解决方案


只要屏幕宽度根据您的钩子逻辑发生变化,只需使用useEffecthideNavItems状态重置回值:matchMediauseMatchMedia

useEffect(() => {
    setHideNavItems(matchMedia);
}, [matchMedia]);

编辑粗体加加林-d76bj


推荐阅读