在功能组件中使用 REST Api 调用更新 React 上下文

Update React Context using a REST Api call in a functional component

我正在尝试使用 API 调用后端 REST API 产生的数据更新 React 应用程序的上下文。问题是我无法同步该功能。

我已经尝试过此博客 post https://medium.com/@__davidflanagan/react-hooks-context-state-and-effects-aa899d8c8014 中建议的解决方案,但它不适用于我的情况。

这是 textContext.js

的代码
import React, {useEffect, useState} from "react";
import axios from "axios";

var text = "Test";

fetch(process.env.REACT_APP_TEXT_API)
    .then(res => res.json())
    .then(json => {
      text = json;
    })




const TextContext = React.createContext(text);
export const TextProvider = TextContext.Provider;
export const TextConsumer = TextContext.Consumer;

export default TextContext

这是我尝试从上下文访问数据的功能组件

import TextProvider, {callTextApi} from "../../../../services/textService/textContext";
function  Profile()
{

  const text = useContext(TextProvider);
  console.log(text);
  const useStyles = makeStyles(theme => ({
    margin: {
      margin: theme.spacing(1)
    }
  }));

我可以看到获取请求在浏览器控制台的网络部分获取数据,但上下文没有得到更新。

我试过在 textContext.js 中这样做。

export async function callTextApi () {
  await fetch(process.env.REACT_APP_TEXT_API)
    .then(res => res.json())
    .then(json => {
      return json;
    })
}


并且我试图使用 useEffect 函数在 Profile.js 中获取数据

 const [text, setText] = useState(null);
  useEffect(()=> {
    setText (callTextApi())
  },[])

这是我第一次使用 React.context 并且非常混乱。我做错了什么或遗漏了什么?

我看到的第一件事是您没有 return 在您的函数中执行承诺,这将导致将状态设置为未定义。

我在下面添加了 return 语句:

export async function callTextApi () {
  return await fetch(process.env.REACT_APP_TEXT_API)
    .then(res => res.json())
    .then(json => {
      return json;
    })
}

您最后的 then-chain 也可以稍微清理一下,我很确定您可以在 return 承诺时删除异步函数中的 await 语句。将自动等待:

export async function callTextApi () {
  return fetch(process.env.REACT_APP_TEXT_API)
    .then(res => res.json())
    .then(json => json)
}

第二步是查看您的 useEffect 挂钩。在 api 调用的承诺得到解决后,您想 setText。所以你也必须让 useEffect 的回调函数异步。

  useEffect(async ()=> {
    const newText = await callTextApi();
    setText (newText);
  },[])

第三步,将查看如何正确使用上下文 api 和 useContext 挂钩。 useContext 挂钩将上下文作为参数,但您将 ContextProvider 作为参数传递。

const text = useContext(TextContext);

上下文和上下文提供者是 React 世界中的两个不同实体。将上下文视为您希望在应用程序中共享的状态和功能(如全局状态),并将提供者视为管理一个上下文并将此上下文状态提供给它的子组件的反应组件。

return(
<TextContext.Provider value={/* some value */}>
    {children}
</TextContext.Provider>);

这是提供程序组件的 return 语句的样子,我认为您的应用程序中目前缺少这段代码。

你这里有很多问题。 fetching 并且应该通过修改 value 属性 在 Provider 内部进行更改。 useContext 接收整个 Context 对象,而不仅仅是 Provider。检查以下内容

//Context.js
export const context = React.createContext()

现在在你的Provider

里面
import { context } from './Context'

const MyProvider = ({children}) =>{
    const [data, setData] = useState(null)
  
    useEffect(() =>{
        fetchData().then(res => setData(res.data))
    },[])
   
   const { Provider } = context
   return(
       <Provider value={data}>
           {children}
       </Provider>
   )
}

现在你有一个 Provider 可以获取一些 data 并将其传递到 value 道具中。要从 functional component 中使用它,请像这样使用 useContext

import { context } from './Context'

const Component = () =>{
    const data = useContext(context)

    return <SomeJSX />
}

记住Component必须在MyProvider

之下

更新

  • 什么是 { children }

Component 声明中的所有内容都映射到 props.children

const App = () =>{
    return(
        <Button>
            Title
        </Button>
    )
}

const Button = props =>{
    const { children } = props

    return(
        <button className='fancy-button'>
            { children /* Title */}
        </button>
    )
}

({ children }) 一样声明它只是 const { children } = props 的快捷方式。我正在使用 children 这样你就可以像这样使用你的 Provider

<MyProvider>
    <RestOfMyApp />
</MyProvider>

这里childrenRestOfMyApp

  • 如何访问 Profile.js 中 Provider 的值?

使用createContext。假设您的 Providervalue 属性 是 {foo: 'bar'}

const Component = () =>{
    const content = useContext(context)

    console.log(content) //{ foo : 'bar' }
}
  • 如何像在 Provider 中那样双重声明常量?

打错了,我改成了MyProvider

从基于 class 的组件内部访问它

class Component extends React.Component{
    render(){
        const { Consumer } = context
        return(
             <Consumer>
                 {
                     context => console.log(contxt) // { foo: 'bar' }
                 }
             </Consumer>
        )
    }
}