skip to Main Content

I’m working on some code that’s using React. It’s added to the page like this:

ReactDom.render(MyClass.render(), document.getElementById('app-id'));

MyClass is defined as a generic class:

import App from "./App";

class MyClass {
  constructor(props) {
    this.props = props;
  }

  render() {
    return App(this.props);
  }
}

window.MyClass = MyClass;

and App is defined (generally) as

const App = (props) => {
    return (
        <Provider store={store} />
    );
};

export default App;

I want to useState within App as I need to pass that state variable and function throughout some code, but the moment that I change the App component to

import { useState } from 'react';

const App = (props) => {
    const [ selectedStore, setSelectedStore ] = useState(store);

    return (
        <Provider store={store} />
    );
};

then I get an error in the console:

Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app

What am I missing to allow the ability to useState within App?

2

Answers


  1. Rules of hooks require that they are called at the top level of a function component or a custom hook. In your case, the App component is defined as a function component, but it’s being used as a regular component by MyClass.render().

    You must change the definition of App to be a functional component.

    import { useState } from 'react';
    
    const App = (props) => {
        const [selectedStore, setSelectedStore] = useState(store);
    
        return (
            <Provider store={store} />
        );
    };
    
    export default App;
    

    And then you use App in MyClass component as before.

    import App from "./App";
    
    class MyClass {
      constructor(props) {
        this.props = props;
      }
    
      render() {
        return <App {...this.props} />;
      }
    }
    
    window.MyClass = MyClass;
    
    Login or Signup to reply.
  2. You are using a react hook, useState can only be called in functional components.
    It is stated in the react official docs that you have to

    Call them at the top level in the body of a function component.

    So what is functional component in React?

    React components are regular JavaScript functions except:

    • Their names always begin with a capital letter.
    • They return JSX markup.

    What is the problem?

    in your case, you are calling the app as function

     render() {
        return App(this.props);
      }
    

    When a function is called directly as Component() it will just run and return something. No lifecycle, no hooks, none of the React magic. It’s very similar to assigning some JSX to a variable, but with more flexibility (you can use if statements, switch, throw, etc.).

    When a functional component is used as <Component /> it will have a lifecycle and can have a state.

    If you have to declare a function that returns JSX inside a functional component or class component and would use one of the react hooks i.e. React.useState

    Solution

    you might call it as in the following

    import React from "react";
    import App from "./App";
    
    export class MyClass extends React.Component {
      constructor(props) {
        super(props);
        this.props = props;
      }
    
      render() {
        return React.createElement(App, this.props);
      }
    }
    
    window.MyClass = MyClass;
    

    OR

    import React from "react";
    import App from "./App";
    
    export class MyClass extends React.Component {
      constructor(props) {
        super(props);
        this.props = props;
      }
    
      render() {
        return <App {...this.props} />;
      }
    }
    
    window.MyClass = MyClass;
    

    References:

    1. Rules of hooks
    2. useState hook
    3. React.createElement
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search