英文:
Setting React state from JSON data in useEffect
问题
我需要获取 json
数据并对其进行迭代,稍后获取更多的 json
数据,最后设置 React 状态。我想使用一个通用函数来处理所有文件请求。我还想处理不同类型的错误(主要是 404
和 json
解析错误)。
我已经阅读了 如何在 useEffect 中使用异步函数 和其他相关文章,但无论我尝试在 fetchData
函数之外做什么,我始终只得到 Promises 而不是数据。
此外,这只需要在主 App.js
加载时执行一次。是否使用 Effect 来实现这个好,还是应该在文件的顶部,在 App
组件本身之前执行(需要这些数据)?
useEffect(() => {
async function fetchData(url) {
const response = await fetch(url, {
headers : {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
})
if (!response.ok) {
return Promise.reject(`File not found: ${url}`)
} else {
const json = await response.json()
.then(res => { return res })
.catch(err => { return Promise.reject(`JSON Error (${url}): ${err}`) })
// 我可以在这里使用 "json"...
}
}
// ...但我需要在这里使用它
const json = fetchData('data/initial-data.json')
.catch(err => console.log('Error ' + err))
// 通过更多的 fetchData 调用迭代其内容
for (const item in json) {
const moreData = fetchData(`data/moredata-${item}.json`)
.catch(err => console.log(`Error: ${err}`))
// 对 moreData 做一些操作
// 比如设置状态
}
}, [])
英文:
I need to fetch json
data and iterate over it, fetching some more json
data later, and finally setting React states. I'd like to use a general function to handle all file requests. I'd also like to handle different types of errors (404
and json
parsing, mostly).
I've read How to use async functions in useEffect and several other relevant articles, but whatever I try to do outside of my fetchData
function, I keep getting only Promises instead of data.
Also, this only needs to be done once, when the main App.js
load. Is it good practice to use an Effect for that, or should it be done at the top of the file, before the App
component itself (which needs the data)?
useEffect(() => {
async function fetchData(url) {
const response = await fetch(url, {
headers : {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
})
if (!response.ok) {
return Promise.reject(`File not found: ${url}`)
} else {
const json = await response.json()
.then(res => { return res })
.catch(err => { return Promise.reject(`JSON Error (${url}): ${err}`) })
// I can use "json" here...
}
}
// ...but I need it here
const json = fetchData('data/initial-data.json')
.catch(err => console.log('Error ' + err))
// to iterate over its contents with more calls to fetchData
for (const item in json) {
const moreData = fetchData(`data/moredata-${item}.json`)
.catch(err => console.log(`Error: ${err}`))
// do something with moreData
// such as setState
}
}, [])
答案1
得分: 1
以下是您要翻译的代码部分:
useEffect(() => {
async function fetchData(url) {
const response = await fetch(url, {
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
})
if (!response.ok) {
throw new Error(`Failed to fetch ${url}, got ${response.status}`)
}
// 注意这里的返回
return await response.json()
}
// 注意需要在这里使用IIFE包装,因为您不能从useEffect回调中返回Promise
(async function () {
try {
const initialData = await fetchData(whatever)
const allData = await Promise.all(initialData.map(async (data) => {
try {
// 获取并返回更多数据
const moreData = await fetchData(data.whatever)
return moreData
} catch (err) {
// 处理错误,可以返回错误或一些哨兵值,比如null
return err
}
}))
// 设置状态,过滤掉失败的部分
setState(allData.filter(x => !(x instanceof Error)))
} catch (err) {
console.error('failed initial fetch')
}
})()
}, [])
另外,注意不要在同一个表达式中混合使用.then
/.catch
和async
/await
,并且不要使用字符串拒绝Promise,因为许多Promise拒绝处理代码隐含地假设导致拒绝的事物是一个实际的错误实例。如果您不会使用throw
,就不要用它来拒绝Promise。
英文:
You just need to return the json from the fetchData function and await
it where you need it:
useEffect(() => {
async function fetchData(url) {
const response = await fetch(url, {
headers : {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
})
if (!response.ok) {
throw new Error(`Failed to fetch ${url}, got ${response.status}`)
}
// note the return
return await response.json()
}
// Note need the IIFE wrapper here because you
// can't return a Promise from the useEffect callback
(async function() {
try {
const initialData = await fetchData(whatever)
const allData = await Promise.all(initialData.map(async (data) => {
try {
// fetch and return more data
const moreData = await fetchData(data.whatever)
return moreData
} catch (err) {
// do something with error, can return it or some
// sentinel value like null
return err
}
}))
// setState, filtering out the failures
setState(allData.filter(x => !(x instanceof Error))
} catch (err) {
console.error('failed initial fetch')
}
})()
}, [])
On a side note don't mix .then
/.catch
with async
/await
in the same expression, and don't reject with strings as many Promise rejection handling code implicitly assumes the thing that caused the rejection is an actual error instance. If you wouldn't throw
it don't reject a Promise with it.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论