This is a knowledge based question. I am new to React coming from a Angular TypeScript class based background. I want to continue using TypeScript when using React but having trouble with compiling hooks.
This code is from the create-react app library and uses a Redux template. It uses a counter component to display a counter.
import React, { useState } from 'react';
import { useAppSelector, useAppDispatch } from '../../app/hooks';
import {
decrement,
increment,
incrementByAmount,
incrementAsync,
incrementIfOdd,
selectCount,
} from './counterSlice';
import styles from './Counter.module.css';
export function Counter() {
const count = useAppSelector(selectCount);
const dispatch = useAppDispatch();
const [incrementAmount, setIncrementAmount] = useState('2');
const incrementValue = Number(incrementAmount) || 0;
return (
<div>
<div className={styles.row}>
<button
className={styles.button}
aria-label="Decrement value"
onClick={() => dispatch(decrement())}
>
-
</button>
<span className={styles.value}>{count}</span>
<button
className={styles.button}
aria-label="Increment value"
onClick={() => dispatch(increment())}
>
</button>
</div>
<div className={styles.row}>
<input
className={styles.textbox}
aria-label="Set increment amount"
value={incrementAmount}
onChange={(e) => setIncrementAmount(e.target.value)}
/>
<button
className={styles.button}
onClick={() => dispatch(incrementByAmount(incrementValue))}
>
Add Amount
</button>
<button
className={styles.asyncButton}
onClick={() => dispatch(incrementAsync(incrementValue))}
>
Add Async
</button>
<button
className={styles.button}
onClick={() => dispatch(incrementIfOdd(incrementValue))}
>
Add If Odd
</button>
</div>
</div>
);
}
This works as expected so I changed it to this
export default class Counter extends React.Component<unknown> {
render(): {
const count = useAppSelector(selectCount);
const dispatch = useAppDispatch();
const [incrementAmount, setIncrementAmount] = useState('2');
const incrementValue = Number(incrementAmount) || 0;
return (
<div>
<div className={styles.row}>
<button
className={styles.button}
aria-label="Decrement value"
onClick={() => dispatch(decrement())}
>
-
</button>
<span className={styles.value}>{count}</span>
<button
className={styles.button}
aria-label="Increment value"
onClick={() => dispatch(increment())}
>
</button>
</div>
<div className={styles.row}>
<input
className={styles.textbox}
aria-label="Set increment amount"
value={incrementAmount}
onChange={(e) => setIncrementAmount(e.target.value)}
/>
<button
className={styles.button}
onClick={() => dispatch(incrementByAmount(incrementValue))}
>
Add Amount
</button>
<button
className={styles.asyncButton}
onClick={() => dispatch(incrementAsync(incrementValue))}
>
Add Async
</button>
<button
className={styles.button}
onClick={() => dispatch(incrementIfOdd(incrementValue))}
>
Add If Odd
</button>
</div>
</div>
);
}
}
But when I compile it - I get this very common error
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:
- You might have mismatching versions of React and the renderer (such as React DOM)
- You might be breaking the Rules of Hooks
- You might have more than one copy of React in the same app See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
Quite obviously my code doesnt adhere to any of the error (which for once, it's quite clear which pleases me). But does this mean when using hooks, I will now lose the export default class syntax?
For context, I used this code in my parent component (App) and it does compile.
import React from 'react';
import './App.css';
import Counter from './features/counter/Counter';
export default class App extends React.Component<unknown> {
render() {
return (
<div className="App">
<header className="App-header">
<Counter />
...
Any info I'd be grateful. Thanks
****** EDIT *******
I see you can use Higher Order Components as explained here
How can I use React hooks in React classic `class` component?
which begs the question, is it wise to use the class based syntax for React now?
CodePudding user response:
Revert to what you had before, hooks are only usable inside React Function components.
// Correct, function component
export function Counter() {
// Incorrect, class component
export default class Counter extends React.Component<unknown> {
render(): {
The linting issue is #2, "breaking the Rules of Hooks". Specifically this is violating Only Call Hooks from React Functions (your second example is a React Class not a React Function).