原篇發問網址在這:https://ithelp.ithome.com.tw/questions/10211232
前言
這篇是我自己發問的,最後搞很久,才終於寫出一個可以發送請求,並拿到數據也成功展示資料給用戶。
因為用這個IT邦才不久,不確定能不能在回答處直接寫代碼,所以乾脆再寫一篇自己解決的後續,分享給如果有跟我一樣卡TS卡很久的夥伴QQ,順便也記錄一下心得留給自己以後能再回顧。
ps. 有些地方我自己覺得還不夠好,像是偷用any之類,感覺有點偷吃步XD,反正只能再繼續多練了QQ"
先上代碼,如果不知道前因後果的人,可以先去上面原發問連結稍微看一下,雖然文很長QQ,謝謝。
(這篇就不附上兩個json檔的內容,怕同頁上下觀看不方便,可以從原文連結去看)
FetchData.tsx
import axios, { AxiosError } from 'axios'
import { MyResponse } from '../components/ShowData'
const FetchData = (url: string) => {
const getData = async () => {
try {
const res = await axios(url)
return res.data
} catch (err) {
return err
}
}
const checkStatus = (promise: Promise<any>) => {
let status = 'pending'
let result: unknown = ''
let suspender = promise.then((r) => {
status = 'success'
result = r
}, (e: AxiosError) => {
status = 'error'
result = e
})
return {
read: () => {
if (status === 'pending') {
throw suspender
} else if (status === 'error') {
return result as MyResponse
}
return result as MyResponse
}
}
}
return {
data: checkStatus(getData())
}
}
export default FetchData
ShowData.tsx
type Props = {
resource: {
data: {
read: () => MyResponse
}
}
}
interface Title<S = string> {
name: S
svgClass: S
path: {[key: string]: S}
}
interface Work<S = string> {
company: S
position: S
year: S
location: S
industry: S
descriptions: {[key: string]: S}
}
interface Education<S = string> {
school_en: S
school_cn: S
year: S
status: S
}
export interface MyResponse {
title: Title
OOO?: Work
XXX?: Work
OXOX?: Work
college?: Education
high_school?: Education
name?: string
message?: string
}
const ShowData = ({resource}: Props) => {
const _data = resource.data.read()
return (
<div>
{_data.name === 'AxiosError' ? _data.message : _data.title.name}
</div>
)
}
export default ShowData
雖然我知道ShowData組件中的MyResponse型別定義這樣寫有很高的機率能夠不報錯,因為等於直接把所有可能的資料(返回的對象)的屬性(properties)全部寫上,那當然就能沒有錯誤,因為有點把東西寫死的概念(但如果今天資料量很龐大的話...QQ)。
原本是有打算這樣寫的:
export interface MyResponse {
[key: string]: Title | Work | Education
}
但不知道為什麼,這樣寫都一直會報錯說,型別不對,不能某某型別賦給某某型別,所以只好放棄
如果可以的話,我是想寫成動態的(像上面那樣簡潔,一行可判斷多種),但暫時寫的項目沒有那麼多的資料需要拿,因此(暫時的解法)就把所有都寫上去,如果有什麼更好的方法的話,還請各位在下面留言分享告訴我,謝謝。
App.tsx
import {Suspense} from 'react'
import FetchData from './helpers/FetchData';
import ShowData from './components/ShowData';
const resource = FetchData("/data/works.json");
function App() {
return (
<div>
<Suspense fallback={<h1>Loading...</h1>}>
<ShowData resource={resource} />
</Suspense>
</div>
);
}
export default App;
以上就是整個代碼了,最後也是成功能展示資料的,包括展示錯誤訊息。
如此一來,我就可以不用自己每個發請求的組件,一直重複寫三個state:isLoading, ErrorMsg, data了。
直接就能依靠React自帶的Suspense組件,幫我判斷是否還在加載中、發生錯誤,或是資料已回來可以展示給用戶,超方便!