How I Fix “Type ‘Void’ is Not Assignable to Type ‘FC‘” in React JS

When I was working on a game project in React JS, I ran into an annoying error that said:

Error Code

 '({ onPickWinner, canPick, timeToPick, hasWinner, revealMode }: PropsWithChildren<IPickWinnerProps>) => void' is not assignable to type 'FC<IPickWinnerProps>'.

At first, I didn’t understand what this meant. But after digging in react js, I figured out the root cause and I’ll walk you through it here step by step.

What I Was Trying to Do

I had a functional component called PickWinnerIdle where I wanted to pass a few props and trigger a function when certain conditions were met. Here’s the interface I defined:

interface IPickWinnerProps {
canPick: boolean;
timeToPick: boolean;
revealMode: boolean;
hasWinner: boolean;
onPickWinner: () => void;
}

Then I wrote the component like this:

const PickWinnerIdle: React.FC<IPickWinnerProps> = ({
onPickWinner,
canPick,
timeToPick,
hasWinner,
revealMode
}) => {
const [pickWinnerLoading, setPickWinnerLoading] = useState(false);
setPickWinnerLoading(true); // Bad idea!
};

And boom, TypeScript yelled at me. The problem? My component wasn’t returning anything — not even <div></div> — so TypeScript assumed it returns void, which is not allowed for components typed as React.FC.

What I Learn

React.FC Must Return JSX

React functional components must return some JSX. Even if it’s just a placeholder, returning void is not valid. So the fix was simple: return something.

Never Call setState in Render

I was also calling setPickWinnerLoading(true) directly in the function body. That’s a mistake. It causes infinite re-renders. Instead, use useEffect.

The Fix Code

Here’s the corrected version of the component:

const PickWinnerIdle: FC<IPickWinnerProps> = ({
onPickWinner,
canPick,
timeToPick,
hasWinner,
revealMode
}) => {
const [pickWinnerLoading, setPickWinnerLoading] = useState(false);

useEffect(() => {
if (canPick && timeToPick && !hasWinner) {
setPickWinnerLoading(true);
onPickWinner();
}
}, [canPick, timeToPick, hasWinner, onPickWinner]);

return (
<div>
<h3>Pick Winner Status</h3>
<p>Can Pick: {canPick ? 'Yes' : 'No'}</p>
<p>Time to Pick: {timeToPick ? 'Yes' : 'No'}</p>
<p>Winner Revealed: {hasWinner ? 'Yes' : 'No'}</p>
<p>Reveal Mode: {revealMode ? 'Enabled' : 'Disabled'}</p>
<p>Loading: {pickWinnerLoading ? 'Loading...' : 'Idle'}</p>
<button disabled={!canPick || hasWinner} onClick={onPickWinner}>
Pick Winner
</button>
</div>
);
};

Practice Features I Added

To make it more interactive and helpful, I added some extras:

Timeout Simulation

I added a 2 second delay to simulate a network call using setTimeout:

useEffect(() => {
if (pickWinnerLoading) {
const timer = setTimeout(() => {
setPickWinnerLoading(false);
}, 2000);
return () => clearTimeout(timer);
}
}, [pickWinnerLoading]);

Parent Component Example

<PickWinnerIdle
canPick={true}
timeToPick={true}
hasWinner={false}
revealMode={true}
onPickWinner={() => console.log("Winner picked")}
/>

Visual Feedback

{pickWinnerLoading && <span>Processing...</span>}

Final Thought

This little debugging session reminded me how TypeScript, though strict, is actually helping catch silly mistakes like forgetting a return. The error message looked scary at first, but once I slowed down and understood the rules of React.FC, it was an easy fix.

Related blog posts