这个问题的前提是落后的,因为我们不应该尝试在 React 之外使用钩子,而是在 React 内部使用外部代码。
快速解决方案:自定义挂钩
如果角色在所有地方都使用,快速自定义挂钩将帮助您入门。这是包装自定义逻辑的最简单方法,因为钩子旨在包装有状态的逻辑以便在组件中重用。
import { useState, useEffect } from "react";
import { useAuth } from "react-oidc-context";
import UserService from "../../../services/authentication/userService";
/**
* Custom hooks that fetches the roles for the logged in user.
*/
const useRoles = () => {
const auth = useAuth();
const [roles, setRoles] = useState();
useEffect(() => {
if (!user) return; // pre-condition
UserService
.getUserRole(auth.user.access_token)
.then(setRoles);
}, [auth.user]);
return roles;
}
然后在任何组件中:
import useRoles from "../useRoles";
const MyExampleComponent = () => {
const roles = useRoles();
if (!roles) return <span>Please login (or something) to see the roles!</span>
return <div>{/* use roles here */}</div>
}
更好的解决方案:服务提供商
如果用户服务上有很多不同的方法需要在整个应用程序中使用,那么在我看来,包装整个服务并通过React 的上下文提供一个现成的版本是最好的。
但首先,让我们UserService
稍微修改一下,使其使用本地 axios 实例而不是全局 axios 实例。
// I also made it a class, but it would also work with an object.
class UserService {
constructor(axios) {
this.axios = axios;
}
getUserRole(){
// use the local axios instance
return this.axios({
method: "get",
// Use the default URL from local axios instance
url: "user/role",
})
.then(({ data }) => data)
.catch(console.log),
}
getSomethingElse() {
// ...
}
}
然后,我们可以为用户服务设置 React 的上下文。
// UserServiceContext.js
import React from 'react';
import { useAuth } from "react-oidc-context";
import UserService from "../../../services/authentication/userService";
const UserServiceContext = React.createContext(null);
// Convenience hook
export const useUserService = () => useContext(UserServiceContext);
// Local axios instance
const axiosInstance = axios.create({
baseURL: 'https://<url>', // set the base URL once here
headers: {
"Authorization": `Bearer ${access_token}`
}
});
const userServiceInstance = new UserService(axiosInstance);
export const UserServiceProvider = (props) => {
const auth = useAuth();
useEffect(() => {
// If the user changes, update the token used by our local axios instance.
axiosInstance.defaults.headers
.common['Authorization'] = auth.user?.access_token;
}, [auth.user]);
return <UserServiceContext.Provider value={userServiceInstance} {...props} />;
}
然后在任何地方,但通常在应用程序的根目录:
import { AuthProvider } from "react-oidc-context";
import { UserServiceProvider } from "./UserServiceContext";
const App = () => (
<AuthProvider>
<UserServiceProvider>
<Content />
</UserServiceProvider>
</AuthProvider>
);
现在一切准备就绪,可以在任何组件中使用!
import { useUserService } from '../UserServiceContext';
const MyExampleComponent = () => {
const userService = useUserService();
const [roles, setRoles] = useState();
// e.g. load roles once on mount.
useEffect(() => {
userService // use the service from the context
.getUserRole() // no auth token needed anymore!
.then(setRoles);
}, []);
if (!roles) return <span>Please login (or something) to see the roles!</span>
return <div>{/* use roles here */}</div>
}
请注意,自定义挂钩仍可用于包装角色获取逻辑。上下文和钩子都可以一起使用来将逻辑包装到每个人自己的偏好中。
// Here's what the hook could look like if it used the new provider above.
const useRoles = () => {
const userService = useUserService();
const [roles, setRoles] = useState();
// e.g. load roles once on mount.
useEffect(() => {
userService // use the service from the context
.getUserRole() // no auth token needed anymore!
.then(setRoles);
}, []);
return roles;
}
我认为提供者解决方案更好,因为它提供了更大的灵活性,同时保持对公开 API 的控制。
在我的解决方案中,我建议使用UserService
实例作为提供的值,但可以更改提供程序以仅公开 API 的一部分,或者它可以自动提供角色和其他数据。由你决定!
免责声明:我使用最少的代码来演示一个有效的解决方案,我的回答可能无法解决您的情况的所有限制。例如, axios 实例可以在提供者内创建useMemo
,同样适用于UserService
实例等。