Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Invariant Violation: Hooks can only be called inside the body of a function component

TL;DR: I'm trying to use the new react-hooks api, but I keep getting an Invariant Violation error when calling the setState hook, but it keeps failing.

import React, { useState } from 'react';

// type State = {
//   heading: string;
// }

const Text = () => {
  const initialState = 'Heading'.toUpperCase();

  const [heading, setHeading] = useState(initialState);

  return (
    <header>
      <h1>
        {setHeading('Brussels')};
        {heading}
      </h1>
    </header>
  );
};

export default Text;
like image 383
intercoder Avatar asked Dec 04 '18 12:12

intercoder


People also ask

Why Hooks can only be called inside of the body of a function component?

Hooks can only be called inside the body of a function component" occurs for multiple reasons: Having a mismatch between the versions of react and react-dom . Having multiple versions of the react package in the same project. Trying to call a component as a function, e.g. App() instead of <App />

Can Hooks be called inside Hooks?

Don't call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function, before any early returns. By following this rule, you ensure that Hooks are called in the same order each time a component renders.

What is an invariant violation?

Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

Can I call Hooks outside component?

You can not use hooks outside a component function, it is simply how they work. But, you can make a composition of hooks. React relies on an amount and order of how hooks appear in the component function.


Video Answer


2 Answers

If you think back in the class component version, your code is calling this.setState in the render() which will trigger another render, and call this.setState again, and the cycle repeats, and you'll get the error:

Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.

You wouldn't call this.setState directly in your render method and neither should you do that using hooks.

It's unclear what you're trying to achieve here, but I think what you want is to set the name only once, which you would do in componentDidMount, and you can use the useEffect hook to achieve that.

Or if you want "Brussels" to be the initial state, pass it as the value into useState().

const {useState, useEffect} = React;

const Text = () => {
  const initialState = 'Heading'.toUpperCase();
  const [heading, setHeading] = useState(initialState);
  useEffect(() => {
    setHeading('Brussels');
  }, []); // Pass in empty array to simulate componentDidMount.
  
  return (
    <header>
      <h1>
        {heading}
      </h1>
    </header>
  );
};

ReactDOM.render(<Text />, document.querySelector('#app'));
<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>
<div id="app"></div>
like image 200
Yangshun Tay Avatar answered Oct 09 '22 21:10

Yangshun Tay


Calling setHeading("Brussel") will cause re rendering again and again which in turns result in an infinite loop, to prevent that you need an event to change the header from "Heading" to "Brussels". Below code might help you

const Text = () => {
const initialState= 'Heading'.toUpperCase();
const [heading, setHeading] = useState(initialState);  
return (
 <header>
    <h1>
    {heading}
    <button onClick= {() =>{setHeading("Brussel")}}>ChangeName</button>
    </h1>
    </header>
);
};
like image 26
jShubh Avatar answered Oct 09 '22 20:10

jShubh