React with Inversify 使 useEffect 无限循环

React with Inversify makes useEffect infinite loop

我正在使用 CRA + TS 并集成了一个用于依赖注入的自定义挂钩 (Inversify)。这是我的代码:

// DI Provider
export const InversifyContext = React.createContext<{ container: Container | null }>({ container: null });

export const DiProvider: React.FC<Props> = (props) => {
  return <InversifyContext.Provider value={{ container: props.container }}>{props.children}</InversifyContext.Provider>;
};


// Custom Hook
export function useInjection<T>(identifier: interfaces.ServiceIdentifier<T>): T {
  const { container } = useContext(InversifyContext);
  if (!container) {
    throw new Error();
  }
  console.log("This is hook"); // This gets printed infinitely
  return container.get<T>(identifier);
}

// Injectable Service
@injectable()
class MyService{
  // some methods
}

// index.tsx
const container = new Container();
container.bind<MyService>("myService").to(MyService);
ReactDOM.render(
  <DiProvider container={container}>
    <MyComponent />,
  document.getElementById("root")
);

// MyComponent.tsx
const MyComponent: React.FC = () => {
  const myService = useInjection<MyService>("myService");
  useEffect(() => {
    myService.getData(); // Loop call
  }, [myService]);
}

现在,当我调试代码时,我看到提供程序正在无限渲染,这导致组件重新渲染。

首先,您需要了解为什么会发生这种情况。

当您在 useEffect 挂钩中使用注入的服务作为依赖项 (您应该这样做),它会触发组件重新呈现,这将调用 useInjection 钩子和 MyService 的 new/updated 实例被 returned,因为实例被更改,useEffect 将再次被触发,这将以递归结束来电。

我同意您不应忽略 useEffect 依赖项。一个简单的解决方案是在挂钩中记忆服务。

所以你的 memoized 钩子将变成:

export function useInjection<T>(identifier: interfaces.ServiceIdentifier<T>): T {
  const { container } = useContext(InversifyContext);
  if (!container) {
    throw new Error();
  }
  console.log("This is hook"); // This gets printed infinitely
  return useMemo(() => container.get<T>(identifier), [container, identifier]);
}

您的挂钩现在将 return 服务实例的记忆版本。