React:在两个组件层次结构之间传递状态的上下文

Posted

技术标签:

【中文标题】React:在两个组件层次结构之间传递状态的上下文【英文标题】:React: Context to pass state between two hierarchies of components 【发布时间】:2019-11-05 12:48:03 【问题描述】:

我正在开发一个网站,我希望能够在应用程序的任何位置访问状态信息。我尝试了几种实现状态的方法,但总是收到以下错误消息:

元素类型无效:应为字符串(对于内置组件)或类/函数(对于复合组件),但得到:未定义。您可能忘记从定义组件的文件中导出组件,或者您可能混淆了默认导入和命名导入。

查看SOS的渲染方法。

这是我的 SOS->index.js 文件:

import React,  useContext  from 'react';
import axios from 'axios';
import CONST from '../utils/Constants';
import  Grid, Box, Container  from '@material-ui/core';
import  styled  from '@material-ui/styles';
import  Header  from '../Layout';
import ListItem from './ListItem';
import SOSButton from './SOSButton';
import FormPersonType from './FormPersonType';
import FormEmergencyType from './FormEmergencyType';
import StateContext from '../App';
import Context from '../Context';

export default function SOS() 
  const  componentType, setComponentType  = useContext(Context);
  const timerOn = false;
  //'type_of_person',
  const ambulance = false;
  const fire_service = false;
  const police = false;
  const car_service = false;

  //static contextType = StateContext;
  const showSettings = event => 
    event.preventDefault();
  ;

  const handleComponentType = e => 
    console.log(e);
    //this.setState( componentType: 'type_of_emergency' );
    setComponentType('type_of_emergency');
  ;

  const handleEmergencyType = new_emergency_state => 
    console.log(new_emergency_state);
    //   this.setState(new_emergency_state);
  ;

  const onSubmit = e => 
    console.log('in OnSubmit');
    axios
      .post(CONST.URL + 'emergency/create', 
        id: 1,
        data: this.state //TODO
      )
      .then(res => 
        console.log(res);
        console.log(res.data);
      )
      .catch(err => 
        console.log(err);
      );
  ;

  let component;

  if (componentType == 'type_of_person') 
    component = (
      <FormPersonType handleComponentType=this.handleComponentType />
    );
   else if (componentType == 'type_of_emergency') 
    component = (
      <FormEmergencyType
        handleComponentType=this.handleComponentType
        handleEmergencyType=this.handleEmergencyType
        emergencyTypes=this.state
        timerStart=this.timerStart
        onSubmit=this.onSubmit
      />
    );
  
  return (
    <React.Fragment>
      <Header title="Send out SOS" />
      <StateContext.Provider value="type_of_person" />
      <Container component="main" maxWidth="sm">
        component
      </Container>
      /*component = (
        <HorizontalNonLinearStepWithError
          handleComponentType=this.handleComponentType
        />*/
    </React.Fragment>
  );

非常感谢您的帮助!

仅供参考,Context文件定义如下:

import React,  useState  from 'react';

export const Context = React.createContext();

const ContextProvider = props => 
  const [componentType, setComponentType] = useState('');
  setComponentType = 'type_of_person';
  //const [storedNumber, setStoredNumber] = useState('');
  //const [functionType, setFunctionType] = useState('');
  return (
    <Context.Provider
      value=
        componentType,
        setComponentType
      
    >
      props.children
    </Context.Provider>
  );
;

export default ContextProvider;

编辑:我已根据您的建议更改了我的代码(上面已更新)。但现在我收到以下错误: TypeError:无法读取未定义的属性“componentType”

【问题讨论】:

你不能通过这种方式改变affected的值。需要使用useState第二个位置返回的函数:const [affected, setAffected] = useState(''); setAffected('type_of_person'); “如果我取消对渲染函数的注释”是什么意思?您正在编写一个功能组件,而不是基于类的组件;它没有render 方法。 您需要阅读函数式组件和类组件! reactjs.org/docs/… 另外,useContext(Context) 会返回一个对象,所以你不能这样做if:componentType == 'type_of_person'。您可以使用const affected: componentType = useContext(Context) 访问它 @Macabeus 谢谢你,你能帮我解决在问题编辑中看到的新错误吗? 【参考方案1】:

Context 不是 ../Context 文件的默认导出,因此您必须将其导入为:

import Context from '../Context';

否则,它会尝试导入您的 Context.Provider 组件。

对于您的文件结构/命名,正确的用法是:

// Main app file (for example)
// Wraps your application in the context provider so you can access it anywhere in MyApp
import ContextProvider from '../Context'

export default () => 
  return (
    <ContextProvider>
      <MyApp />
    </ContextProvider>
  )

// File where you want to use the context
import React,  useContext  from 'react'
import  Context  from '../Context'

export default () => 
  const myCtx = useContext(Context)

  return (
    <div>
      Got this value -  myCtx.someValue  - from context
    </div>
  )

看在上帝的份上...将您的 Context 文件、提供程序和其中的所有内容重命名为更明确的名称。写到这里我都懵了。

【讨论】:

谢谢!除了主应用程序文件之外,如何修改其他文件中的上下文属性? 调用通过上下文传递的 setComponentType 函数。您的上下文(您设置它的方式)是一个对象。因此,当您拨打const myCtx=useContext(Context) 时,您将可以访问myCtx.componentTypemyCtx.setComponentType()。如果它现在坏了,那是因为你的上下文文件中有setComponentType = 'type_of_person';。删除它并使用 const [componentType, setComponentType] = useState('type_of_person') 设置您的初始上下文

以上是关于React:在两个组件层次结构之间传递状态的上下文的主要内容,如果未能解决你的问题,请参考以下文章

React Context API,从子组件设置上下文状态,而不是将函数作为道具传递

在与反应无关的组件之间传递数据

如何在组件之间传递状态属性

React 组件基本使用 ---父子组件之间的通信

尝试在功能组件之间传递状态但出现“未定义”错误

在React Form无状态组件和状态完全Root组件之间传递多个输入字段