【问题标题】:Can react hooks provide hooks?react 钩子可以提供钩子吗?
【发布时间】:2022-01-07 13:51:12
【问题描述】:

这大概可以通过一个简单的实验来验证。我想知道我是否可以让一个钩子提供新的钩子。例如给定以下订阅管理器

export interface SubscriptionManager<T = any> {
  /**
   *
   * @param fn function to call when notified.  It can be passed some data
   * @returns unsubscribe callback.
   */
  subscribe(fn: (data: T) => void): () => void;
  /**
   * Notify subscribers
   * @param data optionally pass data to the subscribers.
   */
  notify(data?: T): void;
}

/**
 * This hook provides a simple subscription semantic to React components.
 */
export function useSubscription<T = any>(): SubscriptionManager<T> {
  const subscribersRef = useRef<((data: T) => void)[]>([]);
  function subscribe(fn: (data: T) => void) {
    subscribersRef.current.push(fn);
    return () => {
      subscribersRef.current = subscribersRef.current.filter(
        (subscription) => !Object.is(subscription, fn)
      );
    };
  }
  function notify(data: T) {
    subscribersRef.current.forEach((fn) => fn(data));
  }
  return { subscribe, notify };
}

我想生成一个钩子useSubcriptionEffect(fn),以防止编写这样的样板代码

useEffect(() => subscribe(fn), []);

Rules of Hooks只讲“通话”,不讲“创作”

【问题讨论】:

  • 我不太确定我是否按照提供的代码 sn-p 遵循您的问题。 useSubcriptionEffect 没有返回任何自定义钩子,它只是返回一个有效的对象(即认为useMemouseCallbackuseStateuseReducer 等... )。 编辑好的,我现在明白你在问什么......你想抽象出必要的useEffect钩子调用。
  • 基本上试图做这样的事情。函数 useSubscribeEffect(fn: (data: T) => void) { useEffect(() => subscribe(fn), []); }

标签: javascript reactjs typescript react-hooks


【解决方案1】:

我尝试使用the experiment 来确认它是否有效并且看起来有效,但由于名称和生成方式的原因,它看起来很奇怪,因为大多数钩子都是imported

代码是

import { useEffect, useRef } from "react";
import { SubscriptionManager } from "./SubscriptionManager";
/**
 * This hook provides a simple subscription semantic to React components.
 */
export function useSubscription<T = any>(): SubscriptionManager<T> {
  const subscribersRef = useRef<((data: T) => void)[]>([]);
  function subscribe(fn: (data: T) => void) {
    subscribersRef.current.push(fn);
    return () => {
      subscribersRef.current = subscribersRef.current.filter(
        (subscription) => !Object.is(subscription, fn)
      );
    };
  }
  function notify(data: T) {
    subscribersRef.current.forEach((fn) => fn(data));
  }
  function useSubscribeEffect(fn: (data: T) => void) {
    useEffect(() => subscribe(fn), []);
  }
  return { subscribe, notify, useSubscribeEffect };
}

测试似乎有效

  it("should notify with clicks using generated hook", async () => {
    const callback = jest.fn();
    const MyContext = createContext<SubscriptionManager>({} as SubscriptionManager);
    function MyContextProvider({ children }: PropsWithChildren<{}>) {
      const { subscribe, notify, useSubscribeEffect } = useSubscription();
      return <MyContext.Provider value={{ subscribe, notify, useSubscribeEffect }}>{children}</MyContext.Provider>
    }
    function MyComponent() {
      const { useSubscribeEffect, notify } = useContext(MyContext);
      function onClick() {
        notify();
      }
      useSubscribeEffect(callback);
      return (<div data-testid="test" onClick={onClick}>abc</div>);
    }
    const { getByTestId } = render(<MyContextProvider><MyComponent /></MyContextProvider>)
    const element = getByTestId("test");
    expect(element.textContent).toEqual("abc");
    await waitFor(() => expect(callback).toBeCalledTimes(0));
    element.click();
    await waitFor(() => expect(callback).toBeCalledTimes(1));
    element.click();
    await waitFor(() => expect(callback).toBeCalledTimes(2));
  })

代码在https://github.com/trajano/react-hooks-tests/tree/useSubscribeEffect 中,在我合并之前仍在尝试查看命名是否有意义。

【讨论】:

  • 我只是想知道你为什么需要它,而不是像 mobx 或 remx 这样的全局存储方法?
  • 因为我不知道你说的那些技术是关于什么的。
猜你喜欢
  • 2021-03-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-08
  • 2021-10-27
  • 2021-04-18
  • 1970-01-01
  • 2013-03-07
相关资源
最近更新 更多