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

IT技术 reactjs fetch global react-hooks
2021-04-28 12:20:42

https://reactjs.org/docs/hooks-custom.html 的这个react示例中,自定义钩子用于 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 应用程序中的不同组件?

4个回答

要在大型项目中跨多个组件共享状态数据,我建议使用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否则 React 无法将更改传递给组件。useStateisOnline

编辑 2useEffect独立于isOnlineState所以钩子不会取消订阅和重新订阅每个变量更改。

在您提到的情况下,该函数在每个组件的渲染中执行。所以每个组件都保持一个独立于其他组件的状态值。对于这个特定的例子,我可能会使用它。

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

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

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

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

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

//useFriendStatusGlobal is a custom hook with globally shared data

const useFriendStatusGlobal = singletonHook(null, useFriendStatus);

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

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

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