Introduction#

GitHub this note show

  • React Ref and UseRef
  • Create a re-useable react component
  • Getting started with Radix-UI

Ref and UseRef#

Let creaet input and expose ref to its parent

interface MyInputProps extends React.InputHTMLAttributes<HTMLInputElement> {
label: string;
}
const MyInput = forwardRef<HTMLInputElement, MyInputProps>(function MyInput(
{ label, ...props },
ref
) {
return (
<label htmlFor="first_name">
{label}
<input ref={ref}></input>
</label>
);
});

Then create a Page in /blog/page.tsx

const BookPage = () => {
const ref = useRef<HTMLInputElement>(null);
function handleClick() {
if (ref.current) {
ref.current.focus();
}
}
return (
<div className="grid gap-6 mb-6 grid-cols-1">
<MyInput ref={ref} label="First Name"></MyInput>
<button
className="bg-green-500 px-10 py-3 rounded-sm"
onClick={handleClick}
>
Edit
</button>
</div>
);
};
export default BookPage;

Reusesable Component#

First we need to install some packages

npm install clsx, tailwind-merge, class-variance-authority

Second, let create a button component

import { cva, type VariantProps } from "class-variance-authority";
import { clsx, type ClassValue } from "clsx";
import React from "react";
import { twMerge } from "tailwind-merge";
function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
const buttonVariants = cva("...", {
variants: {
variant: {
default:
"bg-primary text-primary-foreground shadow-md hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline:
"border border-input hover:bg-accent hover:text-accent-foreground",
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "shadow-none hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 shadow-none hover:underline",
},
size: {
default: "h-8 px-4 py-2",
sm: "h-8 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-8 w-8 p-0",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
});
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean;
}
const MyButton = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
return (
<button
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
>
Hello Button
</button>
);
}
);
MyButton.displayName = "MyButton";
export default MyButton;

Finally, we can use the MyButton somewhere else

<MyButton variant={"outline"} size={"lg"} className="bg-orange-500"></MyButton>

Radix-UI#

To get started with Radix-UI, let create a Dialog component. Here is the basic structure

<Dialog.Root>
<Dialog.Trigger className="rounded p-2 hover:bg-gray-200">
<Pencil1Icon />
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 bg-black/50" />
<Dialog.Content>
<div>
<Dialog.Title></Dialog.Title>
<Dialog.Close></Dialog.Close>
</div>
<div>
<UserFields user={user} />
</div>
<div>
<Dialog.Close></Dialog.Close>
</div>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>

Full implementation

export default function Page() {
return (
<div className="py-10">
<div className="mx-auto max-w-sm space-y-4 rounded-lg bg-gray-200 p-4">
{users.map((user) => (
<div
className="flex justify-between rounded-lg bg-white px-4 py-4 text-gray-900 shadow"
key={user.id}
>
<div>
<p>{user.name}</p>
<p className="text-sm text-gray-500">{user.role}</p>
<p className="text-sm text-gray-500">{user.email}</p>
</div>
<div>
<Dialog.Root>
<Dialog.Trigger className="rounded p-2 hover:bg-gray-200">
<Pencil1Icon />
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 bg-black/50" />
<Dialog.Content className="fixed left-1/2 top-1/2 w-full max-w-md -translate-x-1/2 -translate-y-1/2 rounded-md bg-white p-8 text-gray-900 shadow">
<div className="flex items-center justify-between">
<Dialog.Title className="text-xl">
Edit contact
</Dialog.Title>
<Dialog.Close className="text-gray-400 hover:text-gray-500">
<Cross1Icon />
</Dialog.Close>
</div>
<div className="mt-8">
<UserFields user={user} />
</div>
<div className="mt-8 space-x-6 text-right">
<Dialog.Close className="rounded px-4 py-2 text-sm font-medium text-gray-500 hover:text-gray-600">
Cancel
</Dialog.Close>
<button className="rounded bg-green-500 px-4 py-2 text-sm font-medium text-white hover:bg-green-600">
Save
</button>
</div>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
</div>
</div>
))}
</div>
</div>
);
}

Reference#