view react_games/src/random.tsx @ 177:24fe8ff94056

Fixed few issues with current setup.
author MrJuneJune <me@mrjunejune.com>
date Wed, 21 Jan 2026 19:40:48 -0800
parents 68fa88ac73fe
children
line wrap: on
line source

import { InputEvent, useCallback, useEffect, useRef, useState } from "react";
import ReactDOM from "react-dom/client";

// 1. input and listen to its key stroke fire findSuggestions
// 2. debounce findSuggestions
// 3. create structure for its to search

const DEBOUNCER_WAIT_TIME = 500;

const WORDS = [
  "cat",
  "call",
  "cell",
  "car",
  "carpet",
  "celcius"
]

interface Tries {
  children: Record<string, Tries>;
  words: Set<string>;
}

function TypeAhead() {
  const inputRef = useRef<HTMLInputElement | null>(null);
  const debounceTimerId = useRef<NodeJS.Timeout>(null);
  const tries = useRef<Tries>({ children: {}, words: new Set(WORDS) })
  const [suggestions, setSuggestions] = useState<string[]>([]);

  const performSearch = useCallback((value: string) => {
    let curr = tries.current
    const res: string[] = []
    function dfs(node: Tries, pos: number, off: number = 0, matching: number = 0) {
      if (off > 2) {
        if (matching > Math.ceil(value.length/2)) {
          node.words.forEach(word => res.push(word));
        }
        return;
      }

      if (pos == value.length) {
        node.words.forEach(word => res.push(word));
        return;
      }

      const letter = value[pos];
      if (!node.children[letter]) {
        Object.keys(node.children).forEach((key) => {
          dfs(node.children[key], pos+1, off+1, matching)
        });
      } else {
        dfs(node.children[letter], pos+1, off, matching + 1)
      }

    }
    dfs(curr, 0);
    setSuggestions([...res]);
  }, [])

  const findSuggestions = useCallback(() => {
    if (debounceTimerId.current) {
      clearTimeout(debounceTimerId.current);
    }
    debounceTimerId.current = setTimeout(() => performSearch(inputRef.current?.value || ""), DEBOUNCER_WAIT_TIME)
  }, [])

  useEffect(() => {
    WORDS.forEach(word => {
      let curr = tries.current;
      for (let letter of word) {
        letter = letter.toLowerCase();
        if (!curr.children[letter]) {
          curr.children[letter] = { children: {}, words: new Set([word]) };
        } else {
          curr.children[letter].words.add(word) ;
        }
        curr = curr.children[letter];
      }
    })
    return () => {
      if (debounceTimerId.current)
        clearTimeout(debounceTimerId.current)
    }
  }, [])

  return (
    <>
      <h1>  Type ahead </h1>
      <input ref={inputRef} onChange={findSuggestions}/>
      {suggestions && (
        <div style={{display: "flex", flexDirection: "column", gap: 8, border: "1px black solid"}}>
        {suggestions.map(suggestion => {
          return (
            <div>{suggestion}</div>
          )
        })}
        </div>
      )}
    </>
  );
}

// Render
const root = ReactDOM.createRoot(document.getElementById("root")!);
root.render(<TypeAhead />);