A React TypeScript build wrapper component is a powerful tool that allows developers to customize the rendering of various elements within their applications. This component is designed to wrap around other components and provide additional functionality or customization options.
In this artilces we are also learning Type predicate to render element based on type condition.
In order to keep a well-organized and modular codebase in React applications, it is crucial to create components that can be reused. To achieve this, wrapper components are often employed to render different elements dynamically depending on certain conditions or props. With TypeScript, developers can greatly improve the development process by taking advantage of its type safety and clear interfaces. In this article, we will delve into the design and implementation of such wrapper components in a React TypeScript project.
There are many different ways to use a build wrapper component in a React TypeScript application. For example, you might use it to add custom styling or behavior to a button component, or to create a complex layout for a form that includes multiple input fields and validation logic.
Table of Contents
Understanding the Requirements:
Before diving into the implementation, let’s outline the requirements for our wrapper component:
- It should be a TypeScript functional component.
- The component should accept a prop indicating the type of element to render.
- Based on the value of this prop, the component should render the corresponding HTML element.
- Optional props like className or any other attributes should be supported.
React typescript wrapper component
Let first demonstrate an example for Typescript React conditional rendering based on condition.
import React from 'react';
interface WrapperProps {
condition: boolean;
// Other props as needed
}
const WrapperComponent: React.FC<WrapperProps> = ({ condition, children }) => {
return (
<div>
{condition ? (
<div>
{/* Render element for condition true */}
{children}
</div>
) : (
<div>
{/* Render element for condition false */}
{/* You can also render different components here */}
</div>
)}
</div>
);
};
export default WrapperComponent;
Now in parent component we ca called the wrapper component as.
import WrapperComponent from './WrapperComponent';
const App: React.FC = () => {
const isLoading = true; // Example condition
return (
<WrapperComponent condition={isLoading}>
{isLoading ? (
<p>Loading...</p>
) : (
<div>
<p>Content here</p>
</div>
)}
</WrapperComponent>
);
};
Example 2 React typescript Wrapper component
Here is
import { ComponentPropsWithoutRef } from "react";
//SOLUTION 1
type ButtonProps = {
el: "button";
} & ComponentPropsWithoutRef<"button">;
type AnchorProps = {
el: "anchor";
} & ComponentPropsWithoutRef<"a">;
function Button(props: ButtonProps | AnchorProps) {
const { el, ...otherProps} = props;
if (props.el === "anchor") {
return <a className="button" href="" {...props}></a>;
}
return (
<button className="button" {...props}>
Button
</button>
);
}
export default Button;
There are a few potential issues with this approach:
- Explicitness: The
el
property needs to be explicitly passed every time theButton
component is used. This could be seen as redundant, especially when other properties (likehref
for anchors) are already indicative of the element type. <Button el=”button>… or <Button ele=”anchor” href=”..” > .. - Type Safety: TypeScript cannot guarantee that the correct properties are passed for the specified
el
value. For example, ifel
is set to"anchor"
, but nohref
is provided, TypeScript won’t raise an error, even though an anchor without anhref
is semantically incorrect.
Type Predicate for Inference for above solution
We can leverages the href
prop to infer the element type.
Type Predicate for Inference:
isAnchorProps
function: Acts as a type guard, checking if the given props are of typeAnchorProps
based on the presence of thehref
prop.
The second solution addresses these issues by leveraging TypeScript’s type inference and type guards. The isAnchorProps
function is a type guard that checks if the href
property is present in the props, and if so, infers that the props are of type AnchorProps
.
import { ComponentPropsWithoutRef } from "react";
type ButtonProps = ComponentPropsWithoutRef<"button"> & {
href?: never;
};// not allow href attibute on button
type AnchorProps = ComponentPropsWithoutRef<"a"> & {
href?: string;
}; //Present of href will not allow disabled on Anchor element
function isAnchorProps(props: ButtonProps | AnchorProps): props is AnchorProps {
// Type predicate as return type is return boolean
return "href" in props;
}
function Button(props: ButtonProps | AnchorProps) {
if (isAnchorProps(props)) {
return <a className="button" href="" {...props}></a>;
}
return (
<button className="button" {...props}>
Button
</button>
);
}
export default Button;
This approach has several advantages:
- Implicitness: There’s no need to explicitly specify the element type. It’s inferred from the presence or absence of the
href
property. - Type Safety: TypeScript can guarantee that the correct properties are passed for each element type. If
href
is provided, it expects the other properties ofAnchorProps
to be passed as well, and vice versa forButtonProps
. - Flexibility: This approach is more flexible and scalable. To support another element type, you just need to define a new props type and a corresponding type guard.
<Button>A Button</Button>
<Button href="https://google.com">A Link</Button>
type predicate isAnchorProps
plays a key role in determining the element to render:
- It takes either
ButtonProps
orAnchorProps
as input. - It checks if the
href
prop exists in the provided props. - If
href
exists, it confirms the props are of typeAnchorProps
. - This information is used to conditionally render either an
<a>
(anchor) or<button>
element in theButton
component.