2

I'm building a game. It has a GameResult component:

export default function GameResult({
  scoresData,
  onPlayAgain,
  time,
  place,
}: GameResultProps) {
  return (
    <div className="c-game-result">
      <Leaderboard scoresData={scoresData} />
      <Stats time={time} place={place} />
      <PlayAgain onClick={onPlayAgain} />
    </div>
  );

Here are the interfaces for props that its children have:

  • Leaderboard (shows the data about user scores):
interface Props {
  scoresData: string[];
}
  • Stats (shows time spent on the game, and the place taken):
interface Props {
  time: string;
  place: string;
}
  • PlayAgain (a button to play again):
interface Props {
  onClick: React.MouseEventHandler<HTMLButtonElement>;
}

In the parent component, GameResult, I have GameResultProps that consists of all these interfaces:

interface GameResultProps {
  scoresData: string[];
  onPlayAgain: React.MouseEventHandler<HTMLButtonElement>;
  time: string;
  place: string;
}

My question is: How can I unify all child interfaces into a single props interface for a parent (GameResult), while keeping the interfaces separate for each child?

What I tried:
  • Importing props interface from every child to parent, then writing something like this:
type GameResultProps = StatsProps & LeaderboardProps & PlayAgainProps

It works, however, it seems completely unmaintainable. I have to jump to three files in order to understand what props GameResult should accept. Is there a way to simplify it so that I can access all children props types (scoresData, onPlayAgain, time, place) right in the parent (GameResult) ?

twentysixhugs
  • 324
  • 3
  • 10

1 Answers1

1

You could use a type transformation:

In your GameResult component:

interface GameResultProps {
  scoresData: string[];
  onPlayAgain: React.MouseEventHandler<HTMLButtonElement>;
  time: string;
  place: string;
}

In Stats for example:

type PropsStats = Pick<GameResultProps, "time" | "place">;

Typescript Docs: Utility Types

However to keep it simple you could also just create one type file where you define all three types and combine them to a GameResultProps. Like you did in your post.

Lars Flieger
  • 2,421
  • 1
  • 12
  • 34
  • Let's take this line of code: `type PlayAgainProps = Pick;`. Then, I destructure it in the button like this: `{ onPlayAgain }: PlayAgainProps`. Can I make it somehow so that I destructure it like this: `{OnClick}: PlayAgainProps`? I want it to be called onPlayAgain in the parent, and onClick in the child. What comes to mind is `{ onPlayAgain: onClick }: PlayAgainProps`. However, it doesn't work when I try to pass the prop as ``. – twentysixhugs May 05 '22 at 08:28
  • You have to rename `onPlayAgain` to `onClick`. I would do it the way you said: `{ onPlayAgain: onClick }: PlayAgainProps`. However: Why do you want to rename it? It's probably better from an architecture point to just use `onPlayAgain` always. This makes it much easier. So just go to the element and write `onClick={onPlayAgain}` – Lars Flieger May 05 '22 at 08:33
  • 1
    I thought I should call such things onClick so that the child doesn't know about what the parent does so the child is less coupled with the parent. But if it's better, then alright – twentysixhugs May 05 '22 at 09:00
  • This depends on your architecture / components. If you have a button component which you use over your whole project. The props should be generic like `onClick`. If you have one component like `PlayAgain` you can define the props with more detailed naming. – Lars Flieger May 05 '22 at 09:23