Three 💪 Solid 🪄 Minimalist ⚛️ Components
Tags: react, typescript, webdev, tutorial
Published: 2023-02-07 15:09:39

Using custom React components versus raw HTML while coding makes a huge difference in how fast you get things done.  In this tutorial, I'm going to show you how to build three simple, game-changing React components.

## Copy Button 📄
A simple copy to clipboard button may sound simple, but you'd be surprised how much work you'd be saved if it was a React Component!

![React copy to clipboard button](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ao6d0nr8l2gg2sxkwxuz.gif)

### 1. Create the component
I prefer to use a clean icon from [Feather Icons](https://feathericons.com).  This is the basic structure of our component.

```javascript
import { Copy } from 'react-feather'

export const CopyButton = () => {
  return <button>
    <Copy size={16}/>
  </button>
}
```

### 2. Props
Include `content`, an `onClick` handler, and an `onError` handler.  Copying to clipboard via `window.navigator` returns a promise so we'll need to handle promise rejection errors accordingly.

```javascript
import { Copy } from 'react-feather'

export const CopyButton = ({
  content,
  onClick=()=>{},
  onError
}: {
  content: string;
  onClick?: () => void;
  onError?: (err: Error) => void;
}) => {
  return <button>
    <Copy size={16}/>
  </button>
}
```

### 3. Functionalize
Now we'll create a `copy` function, call the callbacks accordingly (no pun intended), and bind it to the button's `onClick` listener.

```javascript
export const CopyButton = ({
  content,
  onClick=()=>{},
  onError
}: {
  content: string;
  onClick?: () => void;
  onError?: (err: Error) => void;
}) => {
  const copy = () => {
    navigator.clipboard.writeText(content)
      .then(onClick)
      .catch(e => {
        if(onError) {
          onError(e)
        } else {
          console.log(e)
        }
      });
  }
  return <button onClick={copy}>
    <Copy size={16}/>
  </button>
}
```

Add some CSS, and you're all done ✨!

## Auto-Resizing Textarea 🤖📝
Auto-resizing `<textarea>` elements are quite fancy and very helpful if a user has to type a long post or enter a lot of text.

![Auto-resizing textarea](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mhrj4ucrxzk3dfyp7w2u.gif)


### 1. Create the component
Render the `<textarea>` element.

```javascript
export const AutoTextarea = ({ ...props }) => {
  return (<textarea {...props} />)
}
```

### 2. Create a value state
Attach a `value` state so we can have a controlled input

```
import { useState } from "react";

export const AutoTextarea = ({
  ...props
}) => {
  const [value, setValue] = useState("");

  return (
    <textarea
      value={value} 
      onChange={e => setValue(e.target.value)}
      {...props}
    />)
}
```

### 3. Regex Magic
Use a regular expression to search for the `\n` (breakline) character and calculate rows based on that.

```javascript
import { useState } from "react";

export const AutoTextarea = ({
  ...props
}) => {
  const [value, setValue] = useState("");

  return (
    <textarea
      value={value} 
      onChange={e => setValue(e.target.value)}
      rows={/\n/.test(value) ? Number(value.match(/\n/g)?.length) + 1 : 1}
      {...props}
    />)
}
```

Your component should now be ready to roll!

## Scroll Handler 🖱🪄
Controlling element styles based on their scroll position is a very satisfying and beautiful web design trend.  A good example of a site that uses something similar is https://boring.samalander.dev/

![Scroll handler](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/24m8o2n4xm5ookw51azv.gif)

### 1. Create the Component
The wrapper for this component will be a `<div>` element.

```javascript
export const ScrollHandler = () => {
  return <div></div>
}
```

### 2. Props + hooks
The `children` prop passed into this component will be a function with `scrollTop` and `percentage` as parameters.

```javascript
import { ReactNode, useEffect, useState, useRef } from "react";

export const ScrollHandler = ({
  children,
  ...props
}: {
  children: (scroll: number, percentage: number) => ReactNode;
}) => {
  const [scroll, setScroll] = useState(0);
  const scrollRef = useRef<HTMLDivElement>(null);

  return <div {...props} ref={scrollRef}>
    {children(scroll, (window.innerHeight - scroll)/window.innerHeight)}
  </div>
}
```

### 3. Functionalize
Let's attach a `useEffect` hook and a scroll listener to the component.

```javascript
import { ReactNode, useEffect, useState, useRef } from "react";

export const ScrollHandler = ({
  children,
  ...props
}: {
  children: (scroll: number, percentage: number) => ReactNode;
}) => {
  const [scroll, setScroll] = useState(0);
  const scrollRef = useRef<HTMLDivElement>(null);
  
  useEffect(() => {
    if(scrollRef.current) {
      window.addEventListener("scroll", (e) => {
        const clientRect = scrollRef.current?.getBoundingClientRect();
        setScroll(clientRect?.top || 0);
      })  
    }
  }, [scrollRef]);

  return <div {...props} ref={scrollRef}>
    {children(scroll, (window.innerHeight - scroll)/window.innerHeight)}
  </div>
}
```

### 4. Usage
Using this component may be a little confusing if you've never tried using the React `children` prop as a function before.

An example of usage is as follows:

```javascript
<ScrollHandler>
  {(scrollTop, percentage) => <div>
    Position: {scrollTop} pixels. Percent to the top of the 
    viewport: {Math.floor(percentage * 100)}%
  </div>}
</ScrollHandler>
```

As the event listener fires off, `scrollTop` (first parameter) is assigned the value of the absolute `top` of the div element.  `percentage` (second param) is how far the component is to the top of the page.

---

And that's it!  Thanks for reading!

Live demo: https://components.ironcladdev.repl.co/
Source Code: https://replit.com/@IroncladDev/Components

If you've enjoyed this, don't forget to follow me on [Twitter](https://twitter.com/IroncladDev) and [Replit](https://replit.com/@IroncladDev)

Thanks for reading!  Catch y'all next time!