在react中自定义useAxios钩子



我使用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之前,你必须检查loadingerror-

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怎么办?

最新更新