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;
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 />
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.
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.
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.
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>
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>
);
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With