上下文提供程序不会在自定义挂钩状态更改时重新呈现

问题描述 投票:0回答:1

我有以下自定义挂钩来在我的 React 应用程序中全局管理状态:

  export function useAromas() {
   const [state, setState] = useState<UseAromasState>({
    data: [],
    loading: false,
    error: null,
  });

  useEffect(() => {
    console.log("custom hook rerendered because of state change");
  }, [state]);

  const apiUrl = new URL(import.meta.env.VITE_API_URL + "/aromas");

  useEffect(() => {
    const controller = new AbortController();
    const signal = controller.signal;
    fetchAromas(signal);
    return () => controller.abort();
  }, []);

  const fetchAromas = (signal?: AbortSignal) => {
    setState((state) => ({ ...state, loading: true }));
    return customFetch<Aroma[]>({
      url: apiUrl,
      options: { method: "GET", signal },
    }).then((result) => {
      setState({ ...result, loading: false });
      return result.abortController;
     });
  };

  const refreshAromas = () => {
    console.log("refreshing aromas");
    fetchAromas();
  };

  const createAroma = (name: string, brand: Brand) => {
    const request = { name, brand };
    return customFetch<Aroma>({
      url: apiUrl,
      options: {
        method: "POST",
        body: JSON.stringify(request),
        headers: { "Content-Type": "application/json" },
      },
    }).then((response) => {
      console.log(response);
    });
  };

  const deleteAroma = (aromaId: Aroma["id"]): Promise<boolean> => {
    return customFetch({
      url: new URL(apiUrl.toString() + "/" + aromaId),
      options: {
        method: "DELETE",
      },
    })
      .then((response) => {
        console.log("received delete response : ");
        console.log(response);
        refreshAromas();
        return response.data as boolean;
      })
      .catch((error) => {
        console.log(error);
        return false;
      });
  };
  const returnValue = { ...state, refreshAromas, createAroma, deleteAroma };
  console.log(" new return value from useAromas");
  console.log(returnValue);
  return returnValue;
}

我有一个上下文提供者定义如下:

 export const AromasProvider = ({ children }: { children: ReactNode }) 
  => {
  const context = useAromas();

  useEffect(() => {
    console.log("AromasProvider rerendered");
  }, [context]);

  
  console.log("AromasProvider provides ");
  console.log(context);
  return (
    <AromasContext.Provider value={context}>{children} 
 </AromasContext.Provider>
  );
 };

我的问题是,在状态更改时,提供者不会重新渲染,因此不会将状态更改传播给消费者。 在上下文提供程序中记录

useAromas
useEffect
以及依赖于
context
的返回值表明,尽管状态发生变化,提供程序也不会重新渲染。

我确认状态不会传播给消费者,以下消费者日志显示上下文不会反射状态更改:

export function useAromasContext() {
    const aromasContext = useContext(AromasContext);
    console.log("context provided is ");
    console.log(aromasContext);
  if (!aromasContext)
    throw new Error("[useAromasContext ERROR] No AromasContext Provider found");

  return aromasContext;
}

我尝试在上下文提供程序中解构

useAromas()
的结果,以确保我拥有具有新引用的新对象,但它没有改变任何内容。

export const AromasProvider = ({ children }: { children: ReactNode }) => {
  const { refreshAromas, data, createAroma, deleteAroma, error, loading } =
    useAromas();

  useEffect(() => {
    console.log("AromasProvider rerendered");
  }, [data]);

  const context = {
    refreshAromas,
    data,
    createAroma,
    deleteAroma,
    error,
    loading,
  };
  console.log("AromasProvider provides ");
  console.log(context);
  return (
    <AromasContext.Provider value={context}>{children}</AromasContext.Provider>
  );
};

在 @LinDu 测试表明这段代码应该可以工作之后,我甚至重写了钩子和上下文。 我的钩子有 2 个功能

createAroma
deleteAroma
它们看起来如下:

 function deleteAroma(aromaId: string) {
    const deleteUrl = new URL(apiUrl + "/" + aromaId);

    customFetch({ url: deleteUrl, options: { method: "DELETE" } }).then(() =>
      fetchAromas()
    );
  }

  function createAroma(aromaName: string, brand: Brand) {
    const payload = { name: aromaName, brand };

    customFetch({
      url: apiUrl,
      options: {
        method: "POST",
        body: JSON.stringify(payload),
        headers: { "Content-Type": "application/json" },
      },
    }).then((result) => {
      console.log("[CREATED NEW AROMA] " + result.data);
      fetchAromas();
    });
  }

要在成功请求后刷新数据,它们都调用

fetchAromas
,定义如下:

 const fetchAromas = () => {
    const controller = new AbortController();
    const signal = controller.signal;

    customFetch<Aroma[]>({
      url: apiUrl,
      options: { method: "GET", signal },
    }).then((result) => {
      setState((state) => {
        console.log("[FETCHED NEW AROMAS AND SETTING STATE]");
        console.log(result.data?.length);
        return { ...state, aromas: result.data ?? [] };
      });
    });

    return () => controller.abort();
  };

令我困惑的是,当消费者调用

deleteAroma
时,一切都很好:AromasProvider 重新渲染,更新后的状态反映在消费者中。 但是当调用
fetchAromas
时,它就不起作用了。尽管日志记录显示检索到的数据确认请求成功并且状态已更改,但 AromasProvider 不会重新呈现,因此不会传播上下文。 我不知道机器人函数如何在回调中调用完全相同的
fetchAromas
,并且此函数日志显示它在两种情况下都在更新状态。

这是提供者和上下文的样子:

 export const AromasContext = createContext<ReturnType<typeof useAromas>>({
  aromas: [],
  createAroma: () => {
    "createAroma is NOT DEFINED in CONTEXT";
  },
  deleteAroma: () => {
    "creteAroma is NOT DEFINED in CONTEXT";
  },
});

export const AromasProvider = ({ children }: { children: ReactNode }) => {
  const contextValue = useAromas();
  console.log("[AROMAS PROVIDER]contextValue provided : ");
  console.log(contextValue);
  return (
    <AromasContext.Provider value={contextValue}>
      {children}
    </AromasContext.Provider>
  );
};

我完全不知道在哪里寻找可能的原因......

reactjs react-context react-state
1个回答
0
投票

创建新的模拟组件进行进一步测试后,我发现原因是最愚蠢的错误: 原因是消费者调用

createAroma
实际上是直接调用
useAromas
钩子,而不是通过调用
useAromasContext
通过上下文使用它。

© www.soinside.com 2019 - 2024. All rights reserved.