我有一个从 API 获取数据以向用户显示一些详细信息的组件:
const ItemDetail = ({match}) => {
const [item, setItem] = useState(null);
useEffect(() => {
const abort = new AbortController();
fetchItem(abort);
return function cleanUp(){
abort.abort();
}
},[]);
const fetchItem = async (abort) => {
const data = await fetch(`https://fortnite-api.theapinetwork.com/item/get?id=${match.params.id}`, {
signal: abort.signal
});
const fetchedItem = await data.json();
setItem(fetchedItem.data.item);
}
return (
<h1 className="title">{item.name}</h1>
);
}
export default ItemDetail;
但是当导航到达该组件时,控制台显示错误无法访问未定义的名称,可能是因为状态尚未更新。
检查项目是否正确,如果尚未更新则返回空值?像这样:
if(!item) return null;
return (
<h1 className="title">{item.name}</h1>
);
或者在那种情况下应该更好地使用由 React.Component 扩展的类并正确处理其生命周期?
最佳答案
您可以通过以下两种方式之一处理此问题:
让组件以“正在加载”状态呈现自身,或者
在您拥有数据之前不要创建组件——例如,将获取操作移至其父级,并且仅在父级具有渲染它的数据(您将其作为 props 传递)后才创建组件。 (lifting state up的一般原理的具体例子。)
因为这很常见,您可能想要编写一个可以重用的钩子(Hook),而不是每次都必须重新编写逻辑。例如,这里有一个 useFetchJSON
示例,它使用 fetch
来获取和解析您可能在 child 中使用的一些 JSON:
function useFetchJSON(url, init, deps) {
// Allow the user to leave off `init` even if they include `deps`
if (typeof deps == "undefined" && Array.isArray(init)) {
deps = init;
init = undefined;
}
if (!deps) {
console.warn(
"Using `useFetchJSON` with no dependencies array means you'll " +
"re-fetch on EVERY render. You probably want an empty dependency " +
"array instead."
);
}
const [loading, setLoading] = useState(true);
const [data, setData] = useState(undefined);
const [error, setError] = useState(undefined);
useEffect(() => {
setLoading(true);
setData(undefined);
setError(undefined);
fetch(url, init)
.then((response) => {
if (!response.ok) {
throw new Error(`HTTP error ${response.status}`);
}
return response.json();
})
.then(setData)
.catch(setError)
.finally(() => setLoading(false));
}, deps);
return [loading, data, error];
}
用法:
const [loading, data, error] = useFetchJSON(/*...*/, []);
然后使用loading
(加载期间设置的标志),data
(加载的数据,如果它不是undefined
) , 或错误(发生的错误,如果它不是 undefined
):
实例:
const { useState, useEffect } = React;
const fakeData = [
{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
},
{
"userId": 1,
"id": 2,
"title": "quis ut nam facilis et officia qui",
"completed": false
},
{
"userId": 1,
"id": 3,
"title": "fugiat veniam minus",
"completed": false
},
{
"userId": 1,
"id": 4,
"title": "et porro tempora",
"completed": true
},
{
"userId": 1,
"id": 5,
"title": "laboriosam mollitia et enim quasi adipisci quia provident illum",
"completed": false
}
];
function fakeFetch(url, init) {
return new Promise((resolve) => {
// A fake delay to simulate network
setTimeout(() => {
resolve({
ok: true,
status: 200,
json() {
return Promise.resolve(fakeData);
}
});
}, 3000);
});
}
function useFetchJSON(url, init, deps) {
// Allow the user to leave off `init` even if they include `deps`
if (typeof deps == "undefined" && Array.isArray(init)) {
deps = init;
init = undefined;
}
if (!deps) {
console.warn(
"Using `useFetchJSON` with no dependencies array means you'll " +
"re-fetch on EVERY render. You probably want an empty dependency " +
"array instead."
);
}
const [loading, setLoading] = useState(true);
const [data, setData] = useState(undefined);
const [error, setError] = useState(undefined);
useEffect(() => {
setLoading(true);
setData(undefined);
setError(undefined);
fakeFetch(url, init)
.then((response) => {
if (!response.ok) {
throw new Error(`HTTP error ${response.status}`);
}
return response.json();
})
.then(setData)
.catch(setError)
.finally(() => setLoading(false));
}, deps);
return [loading, data, error];
}
// Usage:
function Example() {
const [loading, todos, error] = useFetchJSON("https://jsonplaceholder.typicode.com/todos/", []);
return (
<div>
{loading && <em>Loading todos...</em>}
{error && <strong>Error loading todos: {String(error)}</strong>}
{todos && (
<ul>
{todos.map((todo) => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
)}
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Example />);
<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>
关于javascript - React JS - 如何在更新状态 [Hooks] 之前防止渲染,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57203298/