在非 React 组件中使用钩子的替代方法是什么? [英] What's the alternative to use hooks inside non React component?

查看:25
本文介绍了在非 React 组件中使用钩子的替代方法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是 React 的新手,我有这个功能.

I'm new to React and I have this function.

    import Axios from "axios";
    
    const UserService = {
        getUserRole: (access_token: string = "") => {
            return Axios({
                method: "get",
                url: "https://<url>/user/role",
                headers: {
                    "Authorization": `Bearer ${access_token}`
                }
            }).then((response) => {
                return response.data;
            }).catch((error) => {
                console.log(error);
            });
        }
    }

export default UserService

getUserRole 经常被另一个组件使用,例如

The getUserRole is used constantly by another component, for example

import UserService from "../../../services/authentication/userService";
import { useAuth } from "react-oidc-context";

...

const auth = useAuth();
UserService.getUserRole(auth.user?.access_token);

如您所见,我必须不断地从 useAuth 传递 access_token.有什么方法可以在我的 UserService 中调用 useAuth 这样我就不必经常从我的组件中传递 access_token 吗?

As you can see, I have to constantly pass the access_token from useAuth. Is there any way I can call useAuth inside my UserService so I don't have to constantly pass the access_token from my component?

推荐答案

问题的前提是落后的,因为我们不应该尝试在 React 之外使用钩子,而是在 React 内部使用外部代码.

The premise of the question is backward, as we shouldn't try to use hooks outside of React, but instead use outside code inside of React.

如果角色被到处使用,一个快速的自定义钩子会让你开始.这是包装自定义逻辑的最简单方法,因为 hooks 用于包装有状态逻辑 用于在组件中重用.

If the roles are used all over the place, a quick custom hook will get you started. This is the easiest way to wrap custom logic as hooks are meant to wrap stateful logic for reuse in components.

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 实例.

But first, let's rework the UserService a little so that it uses a local axios instance instead of the global axios instance.

// 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 的上下文.

Then, we can setup the React's context for the user service.

// 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} />;  
}

然后在任何地方,但通常在应用程序的根目录:

Then anywhere, but commonly at the App's root:

import { AuthProvider } from "react-oidc-context";
import { UserServiceProvider } from "./UserServiceContext";

const App = () => (
  <AuthProvider>
    <UserServiceProvider>
      <Content />
    </UserServiceProvider>
  </AuthProvider>
);

现在一切准备就绪,可以在任何组件中使用!

Now everything is ready to be used in any component!

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>
}

请注意,自定义挂钩仍可用于包装角色获取逻辑.上下文和钩子都可以一起使用,将逻辑包装到每个人自己的偏好中.

Note that a custom hook could still be used to wrap the roles fetching logic. Both the context and hooks can be used together to wrap logic to each's own preferences.

// 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 的控制.

I consider the provider solution to be better since it provides more flexibility while keeping control over the exposed API.

在我的解决方案中,我建议使用 UserService 实例作为提供的值,但可以更改提供程序以仅公开 API 的一部分,或者它可以自动提供角色和其他数据.这取决于你!

In my solution, I suggest using the UserService instance as the provided value, but the provider could be changed to expose only parts of the API, or it could provide the roles and other data automatically. It's up to you!

免责声明:我使用最少的代码来演示一个有效的解决方案,我的回答可能无法解决您的情况的所有限制.例如,可以在 useMemo 中的提供者内部创建 axios 实例,对于 UserService 实例等也是如此.

Disclaimer: I've used minimal code to demonstrate a working solution and my answer may not address all constraints of your situation. For example, the axios instance could be created inside the provider within a useMemo, same thing goes for the UserService instance, etc.

这篇关于在非 React 组件中使用钩子的替代方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆