我使用axios与react,所以我想写一个自定义钩子,我做了,它工作得很好,像下面
const useAxios = () => {
const [response, setResponse] = useState([]);
const [error, setError] = useState("");
const [loading, setLoading] = useState(true); //different!
const [controller, setController] = useState();
const axiosFetch = async (configObj) => {
const { axiosInstance, method, url, requestConfig = {} } = configObj;
try {
const ctrl = new AbortController();
setController(ctrl);
const res = await axiosInstance[method.toLowerCase()](url, {
...requestConfig,
});
setResponse(res.data);
} catch (err) {
console.log(err.message);
setError(err.message);
} finally {
setLoading(false);
}
};
useEffect(() => {
console.log(controller);
// useEffect cleanup function
return () => controller && controller.abort();
}, [controller]);
return [response, error, loading, axiosFetch];
};
我还创建了一个axiosInstance来传递BASE_URL和头。现在调用useAxios从api中获取数据,如下所示
const [data, error, loading, axiosFetch] = useAxios();
const getData = () => {
axiosFetch({
axiosInstance: axios,
method: "GET",
url: "/url",
});
};
useEffect(() => {
getData();
}, []);
My Question is
- 当我需要调用一个api时,我正在上面做。
- 但是如果我必须在一个页面中调用三到四个api呢?
- 我是否应该像这样复制代码
const [data1, error1, loading1, axiosFetch]=useAxios();
- 或者有其他方法来最小化代码。
编辑/更新
我运行上面的代码从/url
获取数据,如果我想要击中不同的route
从服务器获得其他工作的更多数据,基本url保持不变
如果第二条路线是/users
const [data, error, loading, axiosFetch] = useAxios();
const getUsers = () => {
axiosFetch({
axiosInstance: axios,
method: "GET",
url: "/users",
});
};
useEffect(() => {
getUsers();
}, [on_BTN_Click]);
上面的代码我想在同一个文件中运行,一个获取数据,一个获取用户,我应该如何写我的axios,因为我认为这个const [data, error, loading, axiosFetch] = useAxios();
应该只调用一次,不知道如何做到这一点或什么是正确的方式,我需要改变我的useAxios
钩子吗?
您可以做的是将端点传递给钩子,或者使用不同的端点正确调用axiosFetch
回调。但我对你的做法有另一种看法关于为什么这个"axios挂钩",我有我的5点看法;可能不是个好主意。
如果你需要封装使用React Hooks的组件逻辑,React Hooks的一个好的经验法则是使用自定义钩子。
React Hooks文档中描述的另一个重要内容是:
自定义钩子是一种重用有状态逻辑的机制(例如设置订阅和记住当前值),但是每次使用自定义钩子时,它内部的所有状态和效果都是完全隔离的。
因此,最终,如果两个不同的组件调用同一个端点的fetch,它们都将执行对后端的调用。如何预防呢?你可以使用像React Query这样的库,它可以创建某种缓存。为您(和一堆其他漂亮的功能!)
最后但并非最不重要的是:API调用更多地与Service
相关。/Module
而不是React Hook(隔离组件逻辑)。我不建议你可以创建一个服务来调用API,并在你的钩子中使用该服务,而不是将逻辑耦合到你的钩子上,并且不得不处理各种各样的问题,比如缓存和同一钩子的多个实例,甚至这个钩子的多个实例调用多个不同的端点,这些端点最终可能依赖或不依赖它们自己。
一个通用的useAsync
钩子如何接受任何异步调用?这就从钩子中解耦了axios的细节。
function useAsync(func, deps = []) {
const [state, setState] = useState({ loading: true, error: null, data: null })
useEffect(
() => {
let mounted = true
func()
.then(data => mounted && setState({ loading: false, error: null, data }))
.catch(error => mounted && setState({ loading: false, error, data: null }))
return () => { mounted = false }
},
deps,
)
return state
}
这里有一个基本用法的例子——
function UserProfile({ userId }) {
const user = useAsync(
() => axios.get(`/users/${userId}`), // async call
[userId], // dependencies
)
if (user.loading)
return <Loading />
if (user.error)
return <Error message={user.error.message} />
return <User user={user.data} />
}
思想是任何异步操作都可以执行。一个更复杂的例子可能像这样-
function UserProfile({ userId }) {
const profile = useAsync(
async () => {
const user = await axios.get(`/users/${userId}`)
const friends = await axios.get(`/users/${userId}/friends`)
const notifications = await axios.get(`/users/${userId}/notifications`)
return {user, friends, notifications}
},
[userId],
)
if (profile.loading) return <Loading />
if (profile.error) return <Error message={profile.error.message} />
return <>
<User user={profile.data.user} />
<Friends friends={profile.data.friends} />
<Notifications notifications={profile.data.notifications} />
</>
}
在最后一个示例中,所有的读取都需要在数据开始呈现之前完成。您可以多次使用useAsync
钩子来获得并行处理。不要忘记,在你可以安全地访问data
之前,你必须检查loading
和error
-
function UserProfile({ userId }) {
const user = useAsync(() => axios.get(`/users/${userId}`), [userId])
const friends = useAsync(() => axios.get(`/users/${userId}/friends`), [userId])
const notifications = useAsync(() => axios.get(`/users/${userId}/notifications`), [userId])
return <>
{ user.loading
? <Loading />
: user.error
? <Error message={user.error.message }
: <User user={user.data} />
}
{ friends.loading
? <Loading />
: friends.error
? <Error message={friends.error.message} />
: <Friends friends={friends.data} />
}
{ notifications.loading
? <Loading />
: notifications.error
? <Error message={notifications.error.message} />
: <Notifications notifications={notifications.data} />
}
</>
}
我建议您也将axios从组件中解耦。您可以通过编写自己的API模块甚至提供useAPI
钩子来实现这一点。如果你对这个问题感兴趣,请看这个问题。
查找TypeScript实现吗?请看这个问题
可以使用
// const { response: loginResponse, error: loginError, loading: loginLoading, customAxios: loginAxios } = useCustomAxios();
// const { hookError, responseStatus, response, hookLoading, customAxios } = useAxios();
// customAxios({
// url: '/users/signup',
// method: 'post',
// headers: {token: AccessToken,},
// data: RegisterData,
// statusToRouteMapping : {409: "/login"}
// });
对于你的问题:-但是如果我必须在一个页面中调用三到四个api怎么办?