javascript 为什么className(带条件运算符)在从本地存储获取新值时不改变?

pinkon5k  于 2023-11-15  发布在  Java
关注(0)|答案(1)|浏览(82)

大意:我有一个菜单,我想在本地存储器中存储指示菜单打开的变量。
首先,我编写了获取和设置LocalStorage变量的函数:

const getItem = (key, defaultValue) => {
    // getting item, if undefined/null return defaultValue
};

const setItem = (key, value) => {
    // just setting item, works perfect
};

字符串
然后我实现了自定义钩子,在那里我使用这些函数。代码看起来像这样:

export const useMenu = () => {
    const isOpened = Boolean(getItem("isOpened", true));
    const toggleMenu = useCallback(() => {
        setItem("isOpened", !isOpened);
    }, [isOpened]);
    
    // ...

    return {
        isOpened,
        toggleMenu,
    };
};


移动到带有组件的主文件。我用钩子获取变量:

const { isOpened, toggleMenu } = useMenu();


然后在主容器中,我有className的条件运算符:

<div className={isOpened ? styles.openedContainer : styles.closedContainer}>


在容器内,我通过单击切换变量:

<div onClick={toggleMenu}>


主要的问题是我的菜单没有关闭!这意味着className没有改变。但同时onClick事件工作得很好,因为我可以在浏览器的控制台中看到isOpened变量会及时改变。有什么原因会发生这种情况吗?

  • 关于localStorage类型:是的,我知道在localStorage中你只能存储字符串。当我从localStorage中获取一个项目时,我将其转换为布尔类型。我在控制台中检查了typeof isOpened,它似乎是一个布尔值。*
const { useCallback } = React;

const fakeStorage = new Map();

const styles = {
    openedContainer: "opened",
    closedContainer: "closed",
};

const getItem = (key, defaultValue) => {
    const value = fakeStorage.get(key);
    // Sadly, Stack Snippets with transpiling enabled don't understand
    // nullish coalescing, have to go old-school
    if (typeof value === "undefined") {
        return defaultValue;
    }
    return value;
};

const setItem = (key, value) => {
    fakeStorage.set(key, value);
};

/*export*/ const useMenu = () => {
    const isOpened = Boolean(getItem("isOpened", true));
    const toggleMenu = useCallback(() => {
        setItem("isOpened", !isOpened);
    }, [isOpened]);
    
    // ...

    return {
        isOpened,
        toggleMenu,
    };
};

const Example = () => {
    const { isOpened, toggleMenu } = useMenu();
    return (
        <div className={isOpened ? styles.openedContainer : styles.closedContainer}>
            <div onClick={toggleMenu}>toggle</div>
        </div>
    );
};

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Example />);
.opened {
    color: green;
}
.closed {
    color: red;
}
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
kcugc4gi

kcugc4gi1#

问题是,这个变化并没有改变React用来知道何时重新渲染组件的任何东西。它超出了React所知道的范围。
你可以通过将它保持在一个state成员中来将它带入React所知道的事情中;这里有一个内置useState的例子:

/*export*/ const useMenu = () => {
    const [isOpened, setIsOpened] = useState(Boolean(getItem("isOpened", true)));

    const toggleMenu = useCallback(() => {
        const updated = !isOpened;
        setItem("isOpened", updated);
        setIsOpened(updated);
    }, [isOpened]);
    
    // ...

    return {
        isOpened,
        toggleMenu,
    };
};

字符串

const { useCallback, useState } = React;

const fakeStorage = new Map();

const styles = {
    openedContainer: "opened",
    closedContainer: "closed",
};

const getItem = (key, defaultValue) => {
    const value = fakeStorage.get(key);
    // Sadly, Stack Snippets with transpiling enabled don't understand
    // nullish coalescing, have to go old-school
    if (typeof value === "undefined") {
        return defaultValue;
    }
    return value;
};

const setItem = (key, value) => {
    fakeStorage.set(key, value);
};

/*export*/ const useMenu = () => {
    const [isOpened, setIsOpened] = useState(Boolean(getItem("isOpened", true)));

    const toggleMenu = useCallback(() => {
        const updated = !isOpened;
        setItem("isOpened", updated);
        setIsOpened(updated);
    }, [isOpened]);
    
    // ...

    return {
        isOpened,
        toggleMenu,
    };
};

const Example = () => {
    const { isOpened, toggleMenu } = useMenu();
    return (
        <div className={isOpened ? styles.openedContainer : styles.closedContainer}>
            <div onClick={toggleMenu}>toggle</div>
        </div>
    );
};

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Example />);
.opened {
    color: green;
}
.closed {
    color: red;
}
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>

有很多方法可以实现这一点。这里,我将本地存储与状态变量并行。您也可以通过useEffect驱动状态变量(并使用回调来更新状态,以避免使用isOpened作为useCallback的依赖项);

/*export*/ const useMenu = () => {
    const [isOpened, setIsOpened] = useState(Boolean(getItem("isOpened", true)));

    useEffect(() => {
        // Update local storage
        setItem("isOpened", isOpened);
    }, [isOpened]);

    const toggleMenu = useCallback(() => {
        setIsOpened((flag) => !flag);
    }, []);
    
    // ...

    return {
        isOpened,
        toggleMenu,
    };
};

const { useCallback, useState, useEffect } = React;

const fakeStorage = new Map();

const styles = {
    openedContainer: "opened",
    closedContainer: "closed",
};

const getItem = (key, defaultValue) => {
    const value = fakeStorage.get(key);
    // Sadly, Stack Snippets with transpiling enabled don't understand
    // nullish coalescing, have to go old-school
    if (typeof value === "undefined") {
        return defaultValue;
    }
    return value;
};

const setItem = (key, value) => {
    fakeStorage.set(key, value);
};

/*export*/ const useMenu = () => {
    const [isOpened, setIsOpened] = useState(Boolean(getItem("isOpened", true)));

    useEffect(() => {
        // Update local storage
        setItem("isOpened", isOpened);
    }, [isOpened]);

    const toggleMenu = useCallback(() => {
        setIsOpened((flag) => !flag);
    }, []);
    
    // ...

    return {
        isOpened,
        toggleMenu,
    };
};

const Example = () => {
    const { isOpened, toggleMenu } = useMenu();
    return (
        <div className={isOpened ? styles.openedContainer : styles.closedContainer}>
            <div onClick={toggleMenu}>toggle</div>
        </div>
    );
};

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Example />);
.opened {
    color: green;
}
.closed {
    color: red;
}
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>

相关问题