React:在 useEffect 中 ES2020 动态导入会阻塞渲染
React: ES2020 dynamic import in useEffect blocks rendering
据我了解,useEffect
中的异步代码运行时不会阻塞渲染进程。所以如果我有这样的组件:
const App = () => {
useEffect(() => {
const log = () => console.log("window loaded");
window.addEventListener("load", log);
return () => {
window.removeEventListener("load", log);
};
}, []);
useEffect(() => {
const getData = async () => {
console.log("begin");
const response = await fetch(
"https://jsonplaceholder.typicode.com/todos/1"
);
const data = await response.json();
console.log("end");
};
getData();
}, []);
return null;
};
控制台输出(按顺序):
begin
window loaded
end
但是,如果我在 useEffect 中使用 ES2020 动态导入:
const App = () => {
useEffect(() => {
const log = () => console.log("window loaded");
window.addEventListener("load", log);
return () => {
window.removeEventListener("load", log);
};
}, []);
useEffect(() => {
const getData = async () => {
console.log("begin");
const data = await import("./data.json");
console.log("end");
};
getData();
}, []);
return null;
};
输出是(按顺序):
begin
end
window loaded
这是一个问题,因为当 data.json
非常大时,浏览器会在 React 渲染任何内容之前导入大文件时挂起。
所有必要和有用的信息都在特里的评论中。根据评论,这里是你想要的实现:
第一个进球:
I would like to import the data after window has loaded for SEO reasons.
第二个进球:
In my case the file I'm trying to dynamically import is actually a function that requires a large dataset. And I want to run this function whenever some state has changed so that's why I put it in a useEffect hook.
开始吧
您可以创建一个函数来获取您创建数据时的数据,如 getData
和 useCallback
挂钩,以便在任何地方使用它而不会出现问题。
import React, {useEffect, useState, useCallback} from 'react';
function App() {
const [data, setData] = useState({});
const [counter, setCounter] = useState(0);
const getData = useCallback(async () => {
try {
const result = await import('./data.json');
setData(result);
} catch (error) {
// do a proper action for failure case
console.log(error);
}
}, []);
useEffect(() => {
window.addEventListener('load', () => {
getData().then(() => console.log('data loaded successfully'));
});
return () => {
window.removeEventListener('load', () => {
console.log('page unmounted');
});
};
}, [getData]);
useEffect(() => {
if (counter) {
getData().then(() => console.log('re load data after the counter state get change'));
}
}, [getData, counter]);
return (
<div>
<button onClick={() => setCounter((prevState) => prevState + 1)}>Increase Counter</button>
</div>
);
}
export default App;
解释:
组件挂载后,事件侦听器将在 'load' 事件上加载 data.json
。至此,第一个目标已经达成。
我使用示例和简单的计数器来演示状态变化和重新加载 data.json
场景。现在,随着 counter
值的每次更改,getData
函数都会调用,因为第二个 useEffect
在其依赖项数组中有计数器。现在第二个目标已经达成。
注意: 第二个 useEffect
中的 if
块阻止 getData
在组件挂载并首次调用 useEffect
注意:不要忘记在失败情况下使用 catch 块。
注意:我把getData
的结果设置为data
状态,你可能对这个结果做不同的处理,但是其他逻辑都一样。
据我了解,useEffect
中的异步代码运行时不会阻塞渲染进程。所以如果我有这样的组件:
const App = () => {
useEffect(() => {
const log = () => console.log("window loaded");
window.addEventListener("load", log);
return () => {
window.removeEventListener("load", log);
};
}, []);
useEffect(() => {
const getData = async () => {
console.log("begin");
const response = await fetch(
"https://jsonplaceholder.typicode.com/todos/1"
);
const data = await response.json();
console.log("end");
};
getData();
}, []);
return null;
};
控制台输出(按顺序):
begin window loaded end
但是,如果我在 useEffect 中使用 ES2020 动态导入:
const App = () => {
useEffect(() => {
const log = () => console.log("window loaded");
window.addEventListener("load", log);
return () => {
window.removeEventListener("load", log);
};
}, []);
useEffect(() => {
const getData = async () => {
console.log("begin");
const data = await import("./data.json");
console.log("end");
};
getData();
}, []);
return null;
};
输出是(按顺序):
begin end window loaded
这是一个问题,因为当 data.json
非常大时,浏览器会在 React 渲染任何内容之前导入大文件时挂起。
所有必要和有用的信息都在特里的评论中。根据评论,这里是你想要的实现:
第一个进球:
I would like to import the data after window has loaded for SEO reasons.
第二个进球:
In my case the file I'm trying to dynamically import is actually a function that requires a large dataset. And I want to run this function whenever some state has changed so that's why I put it in a useEffect hook.
开始吧
您可以创建一个函数来获取您创建数据时的数据,如 getData
和 useCallback
挂钩,以便在任何地方使用它而不会出现问题。
import React, {useEffect, useState, useCallback} from 'react';
function App() {
const [data, setData] = useState({});
const [counter, setCounter] = useState(0);
const getData = useCallback(async () => {
try {
const result = await import('./data.json');
setData(result);
} catch (error) {
// do a proper action for failure case
console.log(error);
}
}, []);
useEffect(() => {
window.addEventListener('load', () => {
getData().then(() => console.log('data loaded successfully'));
});
return () => {
window.removeEventListener('load', () => {
console.log('page unmounted');
});
};
}, [getData]);
useEffect(() => {
if (counter) {
getData().then(() => console.log('re load data after the counter state get change'));
}
}, [getData, counter]);
return (
<div>
<button onClick={() => setCounter((prevState) => prevState + 1)}>Increase Counter</button>
</div>
);
}
export default App;
解释:
组件挂载后,事件侦听器将在 'load' 事件上加载 data.json
。至此,第一个目标已经达成。
我使用示例和简单的计数器来演示状态变化和重新加载 data.json
场景。现在,随着 counter
值的每次更改,getData
函数都会调用,因为第二个 useEffect
在其依赖项数组中有计数器。现在第二个目标已经达成。
注意: 第二个 useEffect
中的 if
块阻止 getData
在组件挂载并首次调用 useEffect
注意:不要忘记在失败情况下使用 catch 块。
注意:我把getData
的结果设置为data
状态,你可能对这个结果做不同的处理,但是其他逻辑都一样。