intercoder
intercoder

Reputation: 2399

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;

Upvotes: 6

Views: 20091

Answers (3)

Justin Herrera
Justin Herrera

Reputation: 653

I upgraded my react version in a certain project so I can use hooks until then I had the same issue and based on the docs the error occurs for mismatching the version of react and react-dom. Upgrading the react-dom works for in my case.

https://reactjs.org/warnings/invalid-hook-call-warning.html

Upvotes: 0

Yangshun Tay
Yangshun Tay

Reputation: 53169

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>

Upvotes: 6

jShubh
jShubh

Reputation: 79

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>
);
};

Upvotes: 1

Related Questions