我有一个自定义钩子useWebApi
,它可以调用 web api 并返回 api 结果。
function useWebApi(method, url) {
const [fetching, setFetching] = useState(true);
const [result, setResult] = useState(null);
useEffect(() => {
axios[method](url).then(res => {
setFetching(false);
setResult(res);
})
}, [url]);
return [fetching, result];
}
我想做这样的事情,获取url1
,如果获取url1
,则获取url2
.这两个 API不是独立的,url2
只能在url1
完成获取时调用。 所以我写了如下代码
const url1 = '/login';
const url2 = '/getMyUserInfo';
function App() {
const [fetching1, result1] = useWebApi('get', url1);
if (result1) {
const [fetching2, result2] = useWebApi('get', url2);
}
return (
<div>
{result1}
{result2}
</div>
)
}
但是 hook 不能在语句内调用if
所以我把它改成,
const url1 = '/login';
const url2 = '/getMyUserInfo';
function App() {
const [fetching1, result1] = useWebApi('get', url1);
useEffect(() => {
if (result1) {
const [fetching2, result2] = useWebApi('get', url2);
}
}, [result1])
return (
<div>
{result1}
{result2}
</div>
)
}
但是,仍然存在问题,我无法渲染result2
,因为它在嵌套范围内,那么我该如何解决呢?
你可以做的是公开一个doFetch
方法,等等,这将允许手动调用钩子。
function useWebApi(method, url) {
const [fetching, setFetching] = useState(true);
const [result, setResult] = useState(null);
const doFetch = useCallback((fetchUrl = url) => {
axios[method](fetchUrl).then(res => {
setFetching(false);
setResult(res);
})
}, [method, url])
useEffect(() => {
if (url) {
doFetch();
}
}, [doFetch]);
return [fetching, result, doFetch];
}
function App() {
const [fetching1, result1, doFetch1] = useWebApi('get', url1);
const [fetching2, result2, doFetch2] = useWebApi('get');
useEffect(() => {
if (result1) {
doFetch2(url2);
}
}, [result1])
return (
<div>
{result1}
{result2}
</div>
)
}
如果第二次调用不依赖于第一次调用的结果值(即它们是两个独立的GET请求),则继续按照钩子的预期在顶层调用它们,并仅在结果一存在时才有选择地呈现结果二。
function App() {
const [fetching1, result1] = useWebApi('get', url1);
const [fetching2, result2] = useWebApi('get', url2);
return (
<div>
{result1}
{result1 ? result2 : null}
</div>
)
}
你可以把条件移到你的钩子里:
function useWebApi(method, url, flag = true) {
const [fetching, setFetching] = useState(true);
const [result, setResult] = useState(null);
useEffect(() => {
if(flag) {
axios[method](url).then(res => {
setFetching(false);
setResult(res);
})
}
}, [url, flag]);
return [fetching, result];
}
function App() {
const [fetching1, result1] = useWebApi('get', url1);
const [fetching2, result2] = useWebApi('get', url2, !!result1);
return (
<div>
{result1}
{result2}
</div>
)
}
我知道这是一个老问题,但将来可能会有人遇到它,在我看来,没有一个答案能解决这里的一般问题(例如,当一个反应钩子的输入是前一个钩子的输出时,如何调用它)。
不要误会我的意思,上面提供的答案是针对此特定问题的良好解决方案,但不能作为规则应用。
交叉依赖钩子的一般问题的解决方案是将组件划分为多个组件并有条件地使用 return 语句。
首先实现一个新的InnerComponent
,它将把你第一次 api 调用的结果作为一个道具。
const url2 = '/getMyUserInfo';
export function InnerComponent() {
const [fetching, result] = useWebApi('get', url2);
return <>{ result }</>
}
然后有条件地渲染这个新组件,这是允许的。
const url1 = '/login';
import { InnerComponent } from './InnerComponent'
function App() {
const [fetching, result] = useWebApi('get', url1);
if (fetching) {
return 'Loading...' // Or some actual Loader component
}
return (
<div>
{result}
{ result && <InnerComponent /> } // Here you can be sure that the result of the first hook call is obtained
</div>
)
}