Home > Enterprise >  Wrap React component and pass props
Wrap React component and pass props

Time:08-05

I'm creating a website where the user can do some exercises, which can be different React components.

An exercise has a base look (title and completion date) that I have in a wrapper class. The actual component will be passed from the outside:

const exercises = [
  <Exercise
    key={1}
    numberInLesson={1}
    title={"Typing the alphabet"}
    ExerciseComponent={TypingTheAlphabet}
    exerciseComponentProps={{
       ...some props to pass to TypingTheAlphabet
    }}

  />,
  <Exercise
    key={2}
    numberInLesson={2}
    title={"Which letter? - Vowels"}
    ExerciseComponent={WhichLetter}
    exerciseComponentProps={{
      ...some props to pass to WhichLetter
    }}

Now my question is, how can I type exerciseComponentProps in Exercise? e.g. if I pass TypingTheAlphabet as ExerciseComponent, how can I ensure only props for TypingTheAlphabet are valid?

Also, I'd like to have a prop onMarkAsCompleted passed from Exercise to the actual component.

For completeness, here is the props interface of Exercise and what I want to accomplish:

export interface ExerciseComponentProps {
  onMarkAsCompleted: () => void;
}


export type ExerciseComponentType = ComponentType<ExerciseComponentProps>;

export interface CommonExerciseProps<C extends ExerciseComponentType> {
  id: string;
  numberInLesson: number;
  title: string;
  ExerciseComponent: C;
  exerciseComponentProps: // how can I type this?
}

const Exercise: React.FC<CommonExerciseProps<any>> = (
  props
) => {
  const [exerciseCompletionDate, setExerciseCompletionDate] =
    useState<Date | null>(null);
  const {
    numberInLesson,
    title,
    ExerciseComponent,
    exerciseComponentProps,
  } = props;

  return (
    <div
      className="w3-padding-small"
      style={exerciseCompletionDate ? { border: "1px solid green" } : {}}
    >
      <h3>
        Exercise {numberInLesson}:
        <br />
        {title}
      </h3>
      {exerciseCompletionDate && (
        <small>
          Completed on {exerciseCompletionDate.toISOString().substring(0, 10)}
        </small>
      )}

      <ExerciseComponent
        onMarkAsCompleted={
          !exerciseCompletionDate
            ? () => setExerciseCompletionDate(new Date())
            : () => {}
        }
        {...exerciseComponentProps}
      />
    </div>
  );
};

export default Exercise;

CodePudding user response:

export interface CommonExerciseProps<C extends ExerciseComponentType> {
  id: string;
  numberInLesson: number;
  title: string;
  ExerciseComponent: C;
  exerciseComponentProps: React.ComponentProps<C>
}

React.ComponentProps<typeof Component>

  • Related