usehooks

repository·main·Indexed 11 days ago

https://github.com/uidotdev/usehooks

A collection of modern, server-safe React hooks for React v18.0.0+ from the ui.dev team. Version 2.4.1 provides pre-built logic for browser APIs, state management, and user interactions, including hooks like useBattery, useCopyToClipboard, useDebounce, and useWindowSize, as well as an experimental set of hooks.

Tokens
29.8K
Snippets
88
Records
117
Agent score
92%

What's inside usehooks

  1. Understand the usehooks.com project structure

    main

    The project is built using Astro. The directory structure follows these conventions:

    • Routing: Astro automatically generates routes based on .astro or .md files located in src/pages/. The filename determines the URL path.
    • Components: Custom components (Astro, React, Vue, Svelte, or Preact) are stored in src/components/.
    • Static Assets: Images and other static files should be placed in the public/ directory.
    • Layouts: Reusable page structures are located in src/layouts/.
    /
    ├── public/           # Static assets (images, favicons)
    ├── src/
    │   ├── components/   # Astro/React/Vue/Svelte/Preact components
    │   ├── layouts/      # Page layouts
    │   ├── pages/        # Routes (based on .astro or .md files)
    │   └── sections/     # Page sections
    └── package.json
  2. Run commands for usehooks.com

    main

    Use the following npm commands to manage the usehooks.com project from the root directory:

    • Install dependencies: npm install
    • Start local development server: npm run dev (runs at localhost:3000)
    • Build production site: npm run build (outputs to ./dist/)
    • Preview production build: npm run preview (to test the build locally before deployment)
    • Run Astro CLI commands: npm run astro <command> (e.g., npm run astro add or npm run astro check)
    • Get Astro CLI help: npm run astro --help
    npm install
    npm run dev
    npm run build
    npm run preview
    npm run astro --help
  3. Install the experimental @uidotdev/usehooks package

    main

    If you want to use the experimental hooks, you must install the experimental version of @uidotdev/usehooks along with the experimental versions of react and react-dom.

    npm i @uidotdev/usehooks@experimental react@experimental react-dom@experimental
  4. Install the standard @uidotdev/usehooks package

    main

    To use the stable collection of modern, server-safe React hooks, install the @uidotdev/usehooks package via npm. This version is compatible with React v18.0.0+.

    npm i @uidotdev/usehooks
  5. Example: Loading MooTools with useScript

    main

    This example demonstrates how to use useScript to load the MooTools library and conditionally render metadata once the script is in the ready state.

    import * as React from "react";
    import { useScript } from "@uidotdev/usehooks";
    import ScriptMeta from './ScriptMeta';
    
    export default function App() {
      const status = useScript(
        `https://cdnjs.cloudflare.com/ajax/libs/mootools/1.6.0/mootools-core.js`,
        {
          removeOnUnmount: false,
        }
      );
    
      React.useEffect(() => {
        if (typeof window.$$ !== "undefined") {
          const id = document.id("moo");
          id.setStyle("background-color", "var(--green)");
        }
      }, [status]);
    
      const isReady = status === "ready";
    
      return (
        <section>
          <h1>useScript</h1>
          <p>
            <span id="moo" className={isReady ? "ready" : ""} />
            <label>Status: {status}</label>
          </p>
          {status === "ready" && (
            <ScriptMeta title="MooTools" status={status} meta={window.MooTools} />
          )}
        </section>
      );
    }
  6. Example: Debouncing a search input

    main

    This example demonstrates how to use useDebounce to trigger a network request (searching Hacker News) only after the user has stopped typing for 300ms, preventing excessive API calls.

    import * as React from "react";
    import { useDebounce } from "@uidotdev/usehooks";
    import searchHackerNews from "./searchHackerNews";
    import SearchResults from "./SearchResults";
    
    export default function App() {
      const [searchTerm, setSearchTerm] = React.useState("js");
      const [results, setResults] = React.useState([]);
      const [isSearching, setIsSearching] = React.useState(false);
      
      // Debounce the search term by 300ms
      const debouncedSearchTerm = useDebounce(searchTerm, 300);
    
      const handleChange = (e) => {
        setSearchTerm(e.target.value);
      };
    
      const handleSubmit = (e) => {
        e.preventDefault();
        const formData = new FormData(e.target);
        setSearchTerm(formData.get("search"));
        e.target.reset();
        e.target.focus();
      };
    
      React.useEffect(() => {
        const searchHN = async () => {
          let results = [];
          setIsSearching(true);
          if (debouncedSearchTerm) {
            const data = await searchHackerNews(debouncedSearchTerm);
            results = data?.hits || [];
          }
    
          setIsSearching(false);
          setResults(results);
        };
    
        searchHN();
      }, [debouncedSearchTerm]);
    
      return (
        <section>
          <header>
            <h1>useDebounce</h1>
            <form onSubmit={handleSubmit}>
              <input
                name="search"
                placeholder="Search HN"
                style={{ background: "var(--charcoal)" }}
                onChange={handleChange}
              />
              <button className="primary" disabled={isSearching} type="submit">
                {isSearching ? "..." : "Search"}
              </button>
            </form>
          </header>
          <SearchResults results={results} />
        </section>
      );
    }
  7. Example: Close a modal when clicking outside

    main

    You can use useEventListener on the document to detect clicks outside of a specific element (like a modal) by checking if the click target is contained within the element's ref.

    import * as React from "react";
    import { useEventListener } from "@uidotdev/usehooks";
    
    export default function App() {
      const ref = React.useRef(null);
      const [isOpen, setIsOpen] = React.useState(false);
    
      const handleClick = (e) => {
        const element = ref.current;
        // If the modal is open and the click was NOT inside the modal element
        if (element && !element.contains(e.target)) {
          setIsOpen(false);
        }
      };
    
      // Attach listener to the entire document
      useEventListener(document, "mousedown", handleClick);
    
      return (
        <section>
          <button onClick={() => setIsOpen(true)}>Open Modal</button>
          {isOpen && (
            <dialog ref={ref}>
              <h2>Modal</h2>
              <p>Click outside to close.</p>
              <button onClick={() => setIsOpen(false)}>Close</button>
            </dialog>
          )}
        </section>
      );
    }
    import * as React from "react";
    import { useEventListener } from "@uidotdev/usehooks";
    import { closeIcon } from "./icons";
    
    export default function App() {
      const ref = React.useRef(null);
      const [isOpen, setIsOpen] = React.useState(false);
    
      const handleClick = (e) => {
        const element = ref.current;
        if (element && !element.contains(e.target)) {
          setIsOpen(false);
        }
      };
    
      useEventListener(document, "mousedown", handleClick);
    
      return (
        <section>
          <h1>useEventListener</h1>
          <div style={{ minHeight: "200vh" }}>
            <button className="link" onClick={() => setIsOpen(true)}>
              Click me
            </button>
          </div>
          {isOpen && (
            <dialog ref={ref}>
              <button onClick={() => setIsOpen(false)}>{closeIcon}</button>
              <h2>Modal</h2>
              <p>
                Click outside the modal to close (or use the button) whatever you
                prefer.
              </p>
            </dialog>
          )}
        </section>
      );
    }
  8. Example: Persisting a drawing with useLocalStorage

    main

    This example demonstrates how to use useLocalStorage to persist a canvas drawing state. The drawing state is stored under the key "drawing" and is automatically updated whenever the drawing changes.

    import * as React from "react";
    import { useLocalStorage } from "@uidotdev/usehooks";
    import createDrawing from "./createDrawing";
    
    export default function App() {
      const [drawing, saveDrawing] = useLocalStorage("drawing", null);
      const ref = React.useRef(null);
    
      React.useEffect(() => {
        createDrawing(ref.current, drawing, saveDrawing);
      }, [drawing, saveDrawing]);
    
      return (
        <section>
          <header>
            <h1>useLocalStorage</h1>
    
            <button className="link" onClick={() => window.location.reload()}>
              Reload Window
            </button>
            <button
              className="link"
              onClick={() => {
                window.localStorage.clear();
                window.location.reload();
              }}
            >
              Clear Local Storage
            </button>
          </header>
          <figure>
            <canvas ref={ref} width={800} height={800} />
            <figcaption>(draw something)</figcaption>
          </figure>
        </section>
      );
    }
  9. Example: Tracking color changes with usePrevious

    main

    This example demonstrates how to use usePrevious to display both the current state and the value that was held immediately before the last update.

    import * as React from "react";
    import { usePrevious } from "@uidotdev/usehooks";
    
    function getRandomColor() {
      const colors = ["green", "blue", "purple", "red", "pink"];
      return colors[Math.floor(Math.random() * colors.length)];
    }
    
    export default function App() {
      const [color, setColor] = React.useState(getRandomColor());
      const previousColor = usePrevious(color);
    
      const handleClick = () => {
        function getNewColor() {
          const newColor = getRandomColor();
          if (color === newColor) {
            getNewColor();
          } else {
            setColor(newColor);
          }
        }
        getNewColor();
      };
    
      return (
        <section>
          <h1>usePrevious</h1>
          <button className="link" onClick={handleClick}>
            Next
          </button>
          <article>
            <figure>
              <p style={{ background: `var(--${previousColor})` }} />
              <figcaption>Previous: {previousColor}</figcaption>
            </figure>
            <figure>
              <p style={{ background: `var(--${color})` }} />
              <figcaption>Current: {color}</figcaption>
            </figure>
          </article>
        </section>
      );
    }
  10. Example: Implementing a periodic color switcher with useInterval

    main

    This example demonstrates how to use useInterval to cycle through an array of colors and how to use the returned clear function to stop the interval via a button click.

    import * as React from "react";
    import { useInterval } from "@uidotdev/usehooks";
    
    const colors = ["green", "blue", "purple", "red", "pink", "beige", "yellow"];
    
    export default function App() {
      const [running, setIsRunning] = React.useState(true);
      const [index, setIndex] = React.useState(0);
    
      const clear = useInterval(() => {
        setIndex(index + 1);
      }, 1000);
    
      const handleStop = () => {
        clear();
        setIsRunning(false);
      };
    
      const color = colors[index % colors.length];
      return (
        <section>
          <h1>useInterval</h1>
          <button disabled={!running} className="link" onClick={handleStop}>
            {running ? "Stop" : "Stopped"}
          </button>
          <div style={{ backgroundColor: `var(--${color})` }} />
        </section>
      );
    }
  11. Example: Implementing a countdown timer with useCountdown

    main

    This example demonstrates how to use useCountdown to create a timer that can be extended by clicking buttons. It uses endTime (a timestamp in milliseconds) and provides callbacks for ticking and completion.

    import * as React from "react";
    import { useCountdown } from "@uidotdev/usehooks";
    
    export default function App() {
      const [endTime, setEndTime] = React.useState(new Date(Date.now() + 10000));
      const [complete, setComplete] = React.useState(false);
    
      const count = useCountdown(endTime, {
        interval: 1000,
        onTick: () => console.log("tick"),
        onComplete: (time) => setComplete(true),
      });
    
      const handleClick = (time) => {
        if (complete === true) return;
        const nextTime = endTime.getTime() + time;
        setEndTime(new Date(nextTime));
      };
    
      return (
        <section>
          <header>
            <h1>useCountdown</h1>
          </header>
          <span className="countdown">{count}</span>
          {complete === false && (
            <div className="button-row">
              <button onClick={() => handleClick(5000)}>+5s</button>
              <button onClick={() => handleClick(10000)}>+10s</button>
              <button onClick={() => handleClick(15000)}>+15s</button>
            </div>
          )}
        </section>
      );
    }
  12. Use the useIdle hook

    main

    The useIdle hook detects user inactivity by tracking interactions and determining if a specified duration of time has passed without activity. It is useful for implementing features like automatic logout, inactivity notifications, or UI adjustments based on engagement.

    Parameters

    NameTypeDescription
    msnumberThe duration of idle time in milliseconds after which the idle state becomes true. The default value is 20000 (20 seconds).

    Return Values

    NameTypeDescription
    idlebooleantrue if the user has been idle for at least ms milliseconds; otherwise false.
    import * as React from "react";
    import { useIdle } from "@uidotdev/usehooks";
    
    export default function App() {
      const idle = useIdle(5000);
      return (
        <section>
          <h1>useIdle</h1>
          <div>
            <span className={idle ? "idle" : ""} />
            <label>Status: {idle ? "Idle" : "Active"}</label>
          </div>
          {idle ? <p>Time to move your mouse</p> : <p>Hold still and wait</p>}
        </section>
      );
    }