【问题标题】:React useContext throws Invalid hook call errorReact useContext 抛出 Invalid hook call 错误
【发布时间】:2019-04-29 03:37:29
【问题描述】:

我正在尝试使用 useContext 将值从上下文提供者传递给消费者,并在渲染函数之外访问该值。

我的提供者如下所示:

export const AppContext = React.createContext();

export class App extends React.Component(){
    render(){
        <AppContext.Provider value={{ name: 'John' }} ><Main /></AppContext>   
    }
}

我的消费者看起来像这样

import React, { useContext } from 'react';
import { AppContext } from './App';

export class Main extends React.Component(){
    componentDidMount(){
        const value = useContext(AppContext);
    }
    render(){
        return (
            <div>Main Component</div>
        )
    }
}

错误是这样的:

挂钩调用无效。 Hooks 只能在函数组件的主体内部调用。


【问题讨论】:

  • 错误清楚地表明钩子只能在函数组件内部调用,并且您正在尝试在基于类的组件内部使用。创建函数组件,它应该可以工作。

标签: javascript reactjs react-hooks


【解决方案1】:

如果你想使用钩子,它们是为函数组件设计的。像这样:

import React, { useContext } from 'react';
import { AppContext } from './App';

const Main = () => {
  const value = useContext(AppContext);

  return(
    <div>Main Component</div>
  );
}

如果您想在基于类的组件中使用它,只需在您的类中将其设置为静态 contextType,然后您可以在组件中将其与 this.context 一起使用,如下所示:

import React from 'react';
import { AppContext } from './App';

class Main extends React.Component(){

  static contextType = AppContext;

  componentDidMount(){
    const value = this.context;
  }
  render(){
    return (
      <div>Main Component</div>
    )
  }
}

编辑: 从您的应用程序组件中删除您的上下文并将其放置在它自己的组件中。我认为您在导出上下文时遇到了冲突。

所以您的应用组件应如下所示:

import React from "react";
import Context from "./Context";
import Main from "./Main";

class App extends React.Component {
  render() {
    return (
      <Context>
        <Main />
      </Context>
    );
  }
}

export default App;

你的主要组件应该是这样的:

import React from "react";
import { AppContext } from "./Context";

class Main extends React.Component {
  static contextType = AppContext;

  render() {
    return <div>{this.context.name}</div>;
  }
}

export default Main;

你的上下文组件应该是这样的:

import React from "react";

export const AppContext = React.createContext();

class Context extends React.Component {
  state = {
    name: "John"
  };

  //Now you can place all of your logic here
  //instead of cluttering your app component
  //using this components state as your context value
  //allows you to easily write funcitons to change
  //your context just using the native setState 
  //you can also place functions in your context value
  //to call from anywhere in your app
  render() {
    return (
      <AppContext.Provider value={this.state}>
        {this.props.children}
      </AppContext.Provider>
    );
  }
}

export default Context;

这是一个沙盒,向您展示它的工作原理CodSandbox

【讨论】:

  • 这在 vs 代码中给出了一个错误:“意外的保留字 'static'。我很想看到这项工作。
  • 我不好,只是将它从构造函数中删除。我编辑了答案,这是一个代码框供您查看它的工作codesandbox.io/s/oo95rnxko5
  • 它给出了这个警告:MainComponent 定义了一个无效的 contextType。 contextType 应该指向 React.createContext() 返回的 Context 对象
  • 我认为您从应用文件导出上下文时遇到冲突。最好的方法是创建一个包装组件并将该组件状态用作您的上下文值。然后,您可以轻松地动态更改上下文,并将所有上下文逻辑保存在其自己的单独组件中。我编辑了答案以向您展示所有组件,并为您提供了一个带有工作示例的沙箱。
  • @SteveK 它工作得非常好但是,如果我在 Context.js 中更改状态,它不会反映在 Main.js 中,我错过了什么吗?
【解决方案2】:

您收到上述错误是因为 Hooks 是用于功能组件而不是类组件,而您尝试在作为类组件的 Main 组件的 componentDidMount 中使用它

您可以使用 useContext 钩子为 Main 组件重写代码,例如

import React, { useContext } from 'react';
import { AppContext } from './App';

export const Main =() =>{
    const value = useContext(AppContext);
    return (
        <div>Main Component</div>
    )
}

或者在类中以不同的方式使用上下文

import React from 'react';
import { AppContext } from './App';

class Main extends React.Component {
    componentDidMount(){
        const value = this.context;
        // use value here. Also if you want to use context elsewhere in class
        // you can use if from this.context
    }
    render(){
        return (
            <div>Main Component</div>
        )
    }
}

Main.contextType = AppContext;

export { Main };

【讨论】:

  • 它给出了这个错误:“MainComponent 定义了一个无效的 contextType。contextType 应该指向 React.createContext() 返回的 Context 对象”用第二种方法。
  • 您能否在 main.js 文件中记录 AppContext 并查看您是否获得了正确的值。一旦按照答案中提到的方式定义了 contextType,也不要忘记导出主文件
  • @Singh,你必须在与 App.js 不同的文件中创建 Context,否则会导致循环依赖,因为 Main 是在 App 中导入的,而 App 是在 Main 中导入的
  • 哦对了!所以上下文应该在父级(Wrapper.js),然后是 App.js,然后是 Main.js?
  • 可能不在包装器中,只是一个不同的文件说 context.js,然后他们从 context.js 将其导入 App 和 Main
【解决方案3】:

Hooks 仅适用于无状态组件。您正在尝试在类组件中使用它。

【讨论】:

    【解决方案4】:

    这是Main.js 文件的内容。如果您想使用基于类的组件而不是功能组件,请取消注释注释部分。

    import React from "react";
    import { AppContext } from "./App";
    
    /** UNCOMMENT TO USE REACT CLASS COMPONENT */
    // class Main extends React.Component() {
    //   render() {
    //     return (
    //       <AppContext.Consumer>
    //         {value => <div>It's Main component. Context value is ${value.name}</div>}
    //       </AppContext.Consumer>
    //     );
    //   }
    // }
    
    const Main = () => {
      const value = React.useContext(AppContext);
    
      return <div>It's Main component. Context value is ${value.name}</div>;
    };
    
    export default Main;

    这是App.js 文件的内容。如果您想使用基于类的组件而不是功能组件,请取消注释注释部分。

    import React from "react";
    import ReactDOM from "react-dom";
    
    import Main from "./Main";
    
    export const AppContext = React.createContext();
    
    /** UNCOMMENT TO USE REACT CLASS COMPONENT */
    // export class App extends React.Component() {
    //   render() {
    //     return (
    //       <AppContext.Provider value={{ name: "John" }}>
    //         <Main />
    //       </AppContext.Provider>
    //     );
    //   }
    // }
    
    const App = () => (
      <AppContext.Provider value={{ name: "John" }}>
        <Main />
      </AppContext.Provider>
    );
    
    const rootElement = document.getElementById("root");
    ReactDOM.render(<App />, rootElement);

    React Hooks 是直接为功能组件实现的,以便让它们成为有状态的。基于类的组件一直都是有状态的,所以你必须使用它们自己的state API。

    工作演示可用here

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-03-20
      • 2022-11-24
      • 2019-11-19
      • 2023-02-08
      • 2019-10-08
      • 2022-06-29
      • 2021-05-27
      • 1970-01-01
      相关资源
      最近更新 更多