当尝试使用上下文时,React抱怨元素类型无效

时间:2020-01-26 12:51:23

标签: reactjs react-context

我正在尝试使用React Context从其他子组件动态更新导航栏标题。我如下创建了NavbarContext.js。我用AdminLayout包装了NavContext.Provider并在useContext中使用了Course.js以动态更新useEffect中的导航栏标题。但是,当我这样做时,react会在屏幕上引发以下错误。

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

如何正确使用上下文,以便可以从Course.js中的useEffect中更新标题标题?

NavbarContext.js

import React, {useState} from 'react'

export default () => {
    const [name,setName] = useState("")

    const NavContext = React.createContext({
        name: "",
        changeName: name => setName(name) 
      })

      const NavProvider = NavContext.Provider
      const NavConsumer = NavContext.Consumer

    return NavContext
}

AdminLayout.js

<NavContext.Provider>
<div className={classes.wrapper}>
      <Sidebar
        routes={routes}
        logoText={"Widubima"}
        logo={logo}
        image={image}
        handleDrawerToggle={handleDrawerToggle}
        open={mobileOpen}
        color={color}
        {...rest}
      />
      <div className={classes.mainPanel} ref={mainPanel}>
        <Navbar
          routes={routes}
          handleDrawerToggle={handleDrawerToggle}
          {...rest}
        />
        {/* On the /maps route we want the map to be on full screen - this is not possible if the content and conatiner classes are present because they have some paddings which would make the map smaller */}
        {getRoute() ? (
          <div className={classes.content}>
            <div className={classes.container}>{switchRoutes}</div>
          </div>
        ) : (
          <div className={classes.map}>{switchRoutes}</div>
        )}
      </div>
    </div>
</NavContext.Provider>

Navbar.js

import NavContext from "context/NavbarContext"

export default function Header(props) {
   function makeBrand() {
    var name;
    props.routes.map(prop => {
      if (window.location.href.indexOf(prop.layout + prop.path) !== -1) {
        name = prop.name;
        document.title = name;
      }
      return null;
    });

    return name;
  }

  return (
    <AppBar className={classes.appBar + appBarClasses}>
      <Toolbar className={classes.container}>
        <div className={classes.flex}>
          {/* Here we create navbar brand, based on route name */}
          <NavContext.Consumer>
            {({ name, setName }) => (
              <Button
                color="transparent"
                href="#"
                className={classes.title}
                style={{ fontSize: "1.5em", marginLeft: "-2%" }}
              >
                {makeBrand() || name}
              </Button>
            )}
          </NavContext.Consumer>
      </Toolbar>
    </AppBar>
  );
}

Course.js

import React, { useState, useEffect, useContext } from "react";
import NavContext from "context/NavbarContext"

const AdminCourse = props => {
  const context = useContext(NavContext);

  useEffect(() => {
   Axios.get('/courses/'+props.match.params.courseId).then(
     res => {
       context.changeName("hello")
       }
   ).catch(err => {
     console.log(err)
   })
    return () => {
      setCourseId("");
    };
  });

  return (
    <GridContainer>
          </GridContainer>
  );
};

export default AdminCourse;

1 个答案:

答案 0 :(得分:2)

我认为您的NavbarContext.js存在问题。 您也不会导出NavContext。 您正在定义提供者,消费者,但您也没有使用它们。

这是解决问题的方法。 首先创建上下文,它是文件中的提供者,如下所示。 NavContext.js

import React, { useState } from "react";
const NavContext = React.createContext();

const NavProvider = props => {
  const [name, setName] = useState("");
  let hookObject = {
    name: name,
    changeName: setName
  };
  return (
    <NavContext.Provider value={hookObject}>
      {props.children}
    </NavContext.Provider>
  );
};

export { NavProvider, NavContext };

在上面的代码中,我首先使用空值创建上下文。 我正在创建NavProvider,它实际上包含值名称作为其中的状态钩。hookObject根据代码中的命名约定公开状态。

现在我出于测试目的,我定义了两个使用者。

一个是我们在useEffect中更新名称的地方,即,

ConsumerThatUpdates.js

import React, { useContext, useEffect } from "react";
import { NavContext } from "./NavContext";
const ConsumerThatUpdates = () => {
  const { changeName } = useContext(NavContext);
  useEffect(() => {
    changeName("NEW NAME");
  }, [changeName]);
  return <div>i update on my useeffect</div>;
};

export default ConsumerThatUpdates;

您可以根据需要更新useEffect。 另一个是我们使用名称的地方

ConsumerThatDisplays.js

import React, { useContext } from "react";
import { NavContext } from "./NavContext";
const ConsumerThatDisplays = () => {
  const { name } = useContext(NavContext);

  return <div>{name}</div>;
};

export default ConsumerThatDisplays;

最后我的App.js看起来像这样,

App.js

import React from "react";
import "./styles.css";
import { NavProvider } from "./NavContext";
import ConsumerThatDisplays from "./ConsumerThatDisplays";
import ConsumerThatUpdates from "./ConsumerThatUpdates";

export default function App() {
  return (
    <div className="App">
      <NavProvider>
        <ConsumerThatDisplays />
        <ConsumerThatUpdates />
      </NavProvider>
    </div>
  );
}

希望这会有所帮助!! 如果您想进一步了解如何有效使用上下文,我建议How to use React Context effectively