React Custom Hooks 全局获取数据并跨组件共享?

Posted

技术标签:

【中文标题】React Custom Hooks 全局获取数据并跨组件共享?【英文标题】:React Custom Hooks fetch data globally and share across components? 【发布时间】:2019-12-27 09:17:34 【问题描述】:

在来自https://reactjs.org/docs/hooks-custom.html 的这个反应示例中,一个自定义钩子在 2 个不同的组件中用于获取用户的在线状态...

function useFriendStatus(friendID) 
  const [isOnline, setIsOnline] = useState(null);

  useEffect(() => 
    function handleStatusChange(status) 
      setIsOnline(status.isOnline);
    

    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => 
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    ;
  );

  return isOnline;

然后它用于以下两个功能:

function FriendStatus(props) 
  const isOnline = useFriendStatus(props.friend.id);

  if (isOnline === null) 
    return 'Loading...';
  
  return isOnline ? 'Online' : 'Offline';


function FriendListItem(props) 
  const isOnline = useFriendStatus(props.friend.id);

  return (
    <li style= color: isOnline ? 'green' : 'black' >
      props.friend.name
    </li>
  );

我的问题是,函数会在导入组件的任何地方单独执行吗?或者,如果它被定义为一个单独的导出函数,是否有像在组件之间共享状态的东西?例如我只执行一次函数,并且“isOnline”状态在所有组件中都相同?

如果它是单独获取的,我将如何在全局范围内仅获取一次数据,然后将其传递给我的 React 应用程序中的不同组件?

【问题讨论】:

【参考方案1】:

要在大型项目中跨多个组件共享状态数据,我建议使用 ReduxReact Context

不过,您可以使用 观察者模式 (https://en.wikipedia.org/wiki/Observer_pattern) 实现全局 isOnline 状态:

// file: isOnline.tsx
import  useEffect, useState  from 'react';

// use global variables
let isOnline = false;
let observers: React.Dispatch<React.SetStateAction<boolean>>[] = [];

// changes global isOnline state and updates all observers
export const setIsOnline = (online: boolean) => 
    isOnline = online;
    observers.forEach(update => update(isOnline));
;

// React Hook
export const useIsOnline = (): [boolean, Function] => 
    const [isOnlineState, setIsOnlineState] = useState<Object>(isOnline);

    useEffect(() => 
        // add setIsOnlineState to observers list
        observers.push(setIsOnlineState);

        // update isOnlineState with latest global isOnline state
        setIsOnlineState(isOnline);

        // remove this setIsOnlineState from observers, when component unmounts
        return () => 
            observers = observers.filter(update => update !== setIsOnlineState);
        ;
    , []);

    // return global isOnline state and setter function
    return [isOnlineState, setIsOnline];

import  useIsOnline  from './isOnline';

function useFriendStatus(friendID) 
  const [isOnline, setIsOnline] = useIsOnline();

  useEffect(() => 
    function handleStatusChange(status) 
      setIsOnline(status.isOnline);
    

    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => 
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    ;
  );

  return isOnline;


function FriendStatus(props) 
  const isOnline = useIsOnline()[0];

  if (isOnline === null) 
    return 'Loading...';
  
  return isOnline ? 'Online' : 'Offline';


function FriendListItem(props) 
  const isOnline = useIsOnline()[0];

  return (
    <li style= color: isOnline ? 'green' : 'black' >
      props.friend.name
    </li>
  );

编辑:在 useIsOnline 内部返回 isOnlineState 创建使用 useState 而不是 isOnline 变量,因为否则 React 无法将更改传递给组件。

编辑 2:使 useEffect 独立于 isOnlineState,因此挂钩不会在每次变量更改时取消订阅和重新订阅。

【讨论】:

【参考方案2】:

在您提到的情况下,该函数在每个组件的渲染时执行。因此,每个组件都独立于其他组件保持状态值。对于这个具体的例子,我可能会使用它。

如果您需要全局共享一些状态数据(如身份验证状态),或在 DOM 树中不同级别的多个组件之间共享,一种选择是使用 React 上下文。

首先,您使用React.createContext() 函数定义一个新的上下文。 查看此链接了解更多信息:https://reactjs.org/docs/context.html

然后,您必须在 DOM 层次结构顶部使用 Context.Provider(一个保存上下文值并管理更新的组件),然后您可以使用钩子 useContext() 来引用上下文值(由 Context 提供提供者)在任何级别的后代组件中。

检查此链接: https://reactjs.org/docs/hooks-reference.html#usecontext

【讨论】:

【参考方案3】:

您可以使用此库将任何自定义挂钩转换为单例 https://www.npmjs.com/package/react-singleton-hook 。 这个库在你的自定义钩子周围创建一个包装器。原始钩子仅安装一次到隐藏组件中。其他组件和自定义钩子使用包装器并将调用委托给您的钩子。

//useFriendStatusGlobal is a custom hook with globally shared data

const useFriendStatusGlobal = singletonHook(null, useFriendStatus);

【讨论】:

【参考方案4】:

每当您使用自定义钩子时,您的应用程序中都会有单独的钩子实例,它们不会共享数据,除非您在其中使用上下文 API,这在多个实例中是通用的,或者您的 ChatAPI 将数据保存在一个中放置在例如单例类实例或 browserStorage/使用 API 中。

useState 或 useReducers 将在您的应用中拥有单独的实例。

您可以简单地将其视为 useState 和 useEffect 在您的代码应用程序中的单个组件中多次编写

【讨论】:

这里的ChatAPI observable 大概将在线状态保存在一个地方并更新订阅者的更改。所以严格来说,上下文 API 并不是在钩子之间共享状态的唯一方法,对象的任何共享实例都可以吗?

以上是关于React Custom Hooks 全局获取数据并跨组件共享?的主要内容,如果未能解决你的问题,请参考以下文章

Make React Easy With These 5 Custom React Hooks

React在工作中对于 Custom React Hooks 一些思考

React在工作中对于 Custom React Hooks 一些思考

gitLab 全局hooks和custom_hooks,以及怎样实现自动pull

React 系列 02❤️ Custom Hooks 中使用 React Context

React 系列 01❤️ 在工作中对于 Custom React Hooks 一些思考