我有以下自定义挂钩来在我的 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>
);
};
我完全不知道在哪里寻找可能的原因......
创建新的模拟组件进行进一步测试后,我发现原因是最愚蠢的错误: 原因是消费者调用
createAroma
实际上是直接调用 useAromas
钩子,而不是通过调用 useAromasContext
通过上下文使用它。