每次 HTML lang 属性更改时,我都在编写自定义钩子来设置新的语言环境,但是当使用 javascript 更改 useEffect
时,似乎 document.documentElement.lang
钩子不会触发。我知道如何解决这个问题,我的问题是为什么它会这样?
export const useLocale = (): LocaleObject => {
const [lang, setLang] = useState<string>(document.documentElement.lang);
useEffect(() => {
setLang(document.documentElement.lang);
}, [document.documentElement.lang]); // useEffect is not triggered when document.documentElement.lang changes
return locale[lang];
};
答案 0 :(得分:1)
正如@DennisVash 在他的 comment 中所写:
<块引用>为什么?因为对 document.documentElement.lang 的更改不会触发 钩子。只有渲染会触发钩子,如果 lang 改变了 回调将被执行。
但是,由于更改属性实际上会更改 DOM 中的 lang
属性值,因此您可以使用 MutationObserver 来跟踪 lang
属性值。
我创建了一个自定义的 useMutationObserver
钩子来跟踪 DOM 中的变化,并以 useLocale
为基础。
const { useRef, useEffect, useState, useCallback } = React;
const useMutationObserver = (domNodeSelector, observerOptions, cb) => {
useEffect(() => {
const targetNode = document.querySelector(domNodeSelector);
const observer = new MutationObserver(cb);
observer.observe(targetNode, observerOptions);
return () => {
observer.disconnect();
};
}, [domNodeSelector, observerOptions, cb]);
}
const options = { attributes: true };
const useLocale = () => {
const [lang, setLang] = useState(document.documentElement.lang);
const handler = useCallback(mutationList => {
mutationList.forEach(mutation => {
if(mutation.type !== 'attributes' || mutation.attributeName !== 'lang') return;
setLang(document.documentElement.lang);
});
}, []);
useMutationObserver('html', options, handler);
return lang; // locale[lang]
};
const Demo = () => {
const locale = useLocale();
return <div>{locale}</div>;
};
document.documentElement.lang = 'en'; // base lang
ReactDOM.render(
<Demo />,
root
);
// example - changing the lang
setTimeout(() => document.documentElement.lang = 'fr', 1000);
setTimeout(() => document.documentElement.lang = 'ru', 3000);
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<div id="root"></div>