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”):