使用axios.get 是低级的,需要你连接一堆额外的东西才能让事情正常工作。相反,尝试编写自定义钩子来抽象这个逻辑 -
const identity = x => x
const useAsync = (runAsync = identity, deps = []) => {
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
const [result, setResult] = useState(null)
useEffect(_ => {
Promise.resolve(runAsync(...deps))
.then(setResult, setError)
.finally(_ => setLoading(false))
}, deps)
return { loading, error, result }
}
使用useAsync 看起来像这样 -
const MyApp = () => {
const { loading, error, result } =
useAsync(_ => axios.get("./foo.json").then(res => res.json()))
if (loading)
return <p>loading...</p>
if (error)
return <p>error: {error.message}</p>
return <pre>result: {result}</pre>
}
但是您可能会有很多获取 JSON 的组件,对吧?我们可以制作一个更高级别的自定义钩子,useJSON,它是useAsync 的特化——
const fetchJson = (url = "") =>
axios.get(url).then(r => r.json()) // <-- stop repeating yourself
const useJson = (url = "") =>
useAsync(fetchJson, [url]) // <-- useAsync
const MyApp = () => {
const { loading, error, result } =
useJson("./foo.json") // <-- dead simple
if (loading)
return <p>loading...</p>
if (error)
return <p>error: {error.message}</p>
return <pre>result: {result}</pre>
}
查看此功能代码 sn-p 中的自定义挂钩 -
const { useState, useEffect } =
React
// fake fetch slows response down so we can see loading
const _fetch = (url = "") =>
fetch(url).then(x =>
new Promise(r => setTimeout(r, 2000, x)))
const identity = x => x
const useAsync = (runAsync = identity, deps = []) => {
const [loading, setLoading] = useState(true)
const [error, setError] = useState(null)
const [result, setResult] = useState(null)
useEffect(_ => {
Promise.resolve(runAsync(...deps))
.then(setResult, setError)
.finally(_ => setLoading(false))
}, deps)
return { loading, error, result }
}
const fetchJson = (url = "") =>
_fetch(url).then(r => r.json())
const useJson = (url = "") =>
useAsync(fetchJson, [url])
const MyComponent = ({ url = "" }) => {
const { loading, error, result } =
useJson(url)
if (loading)
return <pre>loading...</pre>
if (error)
return <pre style={{color: "tomato"}}>error: {error.message}</pre>
return <pre>result: {JSON.stringify(result, null, 2)}</pre>
}
const MyApp = () =>
<main>
ex 1 (success):
<MyComponent url="https://httpbin.org/get?foo=bar" />
ex 2 (error):
<MyComponent url="https://httpbin.org/status/500" />
</main>
ReactDOM.render(<MyApp />, document.body)
pre {
background: ghostwhite;
padding: 1rem;
white-space: pre-wrap;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>