Javascript 在React中使用自定义挂钩获取多个表中的数据时,自定义ContextProvider状态出现问题 我有多个小的表,当应用程序更新了相应的表时,我希望能够写/读/更新我的组件(我们现在可以认为它是一个单用户应用程序)。
这个问题激发了我的灵感,我编写了一个自定义的提供者和相关的钩子,用于在我的应用程序中获取数据(并最终发布): 我想到了这个:Javascript 在React中使用自定义挂钩获取多个表中的数据时,自定义ContextProvider状态出现问题 我有多个小的表,当应用程序更新了相应的表时,我希望能够写/读/更新我的组件(我们现在可以认为它是一个单用户应用程序)。,javascript,reactjs,react-hooks,Javascript,Reactjs,React Hooks,这个问题激发了我的灵感,我编写了一个自定义的提供者和相关的钩子,用于在我的应用程序中获取数据(并最终发布): 我想到了这个: import React from "react"; import { useContext, useState, useEffect } from "react"; import axios from "axios"; const MetadataContext = React.createContext()
import React from "react";
import { useContext, useState, useEffect } from "react";
import axios from "axios";
const MetadataContext = React.createContext();
function MetadataContextProvider(props) {
let [metadata, setMetadata] = useState({});
async function loadMetadata(url) {
let response = await axios.get(url);
// here when I console.log the value of metadata I get {} all the time
setMetadata({ ...metadata, [url]: response.data });
}
async function postNewItem(url, payload) {
await axios.post(url, payload);
let response = await axios.get(url);
setMetadata({ ...metadata, [url]: response.data });
}
return (
<MetadataContext.Provider value={{ metadata, loadMetadata, postNewItem }}>
{props.children}
</MetadataContext.Provider>
);
}
function useMetadataTable(url) {
// this hook's goal is to allow loading data in the context provider
// when required by some component
const context = useContext(MetadataContext);
useEffect(() => {
context.loadMetadata(url);
}, []);
return [
context.metadata[url],
() => context.loadMetadata(url),
(payload) => context.postNewItem(url, payload),
];
}
function TestComponent({ url }) {
const [metadata, loadMetadata, postNewItem] = useMetadataTable(url);
// not using loadMetadata and postNewItem here
return (
<>
<p> {JSON.stringify(metadata)} </p>
</>
);
}
function App() {
return (
<MetadataContextProvider>
<TestComponent url="/api/capteur" />
<br />
<TestComponent url="/api/observation" />
</MetadataContextProvider>
);
}
export default App;
从“React”导入React;
从“react”导入{useContext,useState,useEffect};
从“axios”导入axios;
常量MetadataContext=React.createContext();
函数MetadataContextProvider(props){
let[metadata,setMetadata]=useState({});
异步函数loadMetadata(url){
let response=等待axios.get(url);
//在这里,当我console.log元数据的值时,我总是得到{}
setMetadata({…元数据,[url]:response.data});
}
异步函数postNewItem(url、负载){
等待axios.post(url,有效负载);
let response=等待axios.get(url);
setMetadata({…元数据,[url]:response.data});
}
返回(
{props.children}
);
}
函数UseMataDataTable(url){
//这个钩子的目标是允许在上下文提供程序中加载数据
//当某些组件需要时
const context=useContext(MetadataContext);
useffect(()=>{
加载元数据(url);
}, []);
返回[
context.metadata[url],
()=>context.loadMetadata(url),
(有效负载)=>context.postNewItem(url,有效负载),
];
}
函数TestComponent({url}){
const[metadata,loadMetadata,postNewItem]=UseMatadataTable(url);
//此处不使用loadMetadata和postNewItem
返回(
{JSON.stringify(元数据)}
);
}
函数App(){
返回(
);
}
导出默认应用程序;
(代码应该在CRA上下文中运行,两个API几乎都可以用任何API替换)
当我运行它时,在两个端点(/api/capteur和/api/observation)上都会触发一个请求,但是当我希望MetadataContextProvider中的元数据对象有两个键:“/api/capteur”和“/api/observation”时,只会显示最后一个请求的内容
在loadMetadata函数中输入console.log元数据时,元数据始终具有初始状态挂钩值,即{}
我还没有反应过来,我努力了,但我真的不知道这里发生了什么。有人能帮忙吗?您的问题是如何使用
setMetadata
更新metadata
对象。
通过上下文中的loadMetadata
更新metadata
对象的操作分别由两个“实例”完成:TestComponent
#1和TestComponent
#2。
它们都可以访问上下文中的元数据
对象,但它们不是即时同步的,因为useState
的setter函数是异步工作的
你的问题的简单解决方法叫做。
useState
的setter也提供了一个回调函数,它将使用(我在这里过于简化)最新的状态
在您的上下文提供程序中:
async function loadMetadata(url) {
let response = await axios.get(url);
setMetadata((existingData) => ({ ...existingData, [url]: response.data }));
// instead of
// setMetadata({ ...metadata, [url]: response.data });
}
下面是一个工作代码沙盒:
查看控制台以查看执行顺序
我强烈推荐,尤其是“hooksapi参考”。您的代码还存在其他问题(例如,
useffect
hook中缺少依赖项,是否启用了ESLint?)
如果您想更好地了解如何使用React的上下文,我可以推荐Kent C.Dodds的博客:
它就像一个符咒,谢谢你。我已经浏览了hooks文档,但看起来我又漏掉了一些有用的信息。我确实启用了ESLint,它指出了缺少的依赖项,当useEffect的第二个参数是[context,url]时,我很高兴。但如果我这样做,我最终会陷入一个无限的更新循环。我该怎么办?@HugoMerzisen无限循环是由上下文的
loadMetadata
函数引起的。每个重新加载程序都将重新创建函数,这将导致另一个重新加载程序,依此类推。。。这本身就是一个话题。您可以通过对上下文值使用useMemo
(此值:
),或者在您的情况下,对loadMetadata
函数使用useCallback
来修复此问题,这将导致不在每次渲染时创建函数(Google“useCallback referential equality”):