reboottime / React-Develpment

Some notes, thought, articles or source code readings aggregated here about React Web development.
0 stars 0 forks source link

[React FAQs] Frequently asked hooks #86

Open reboottime opened 1 month ago

reboottime commented 1 month ago

Overview

This article collected some frequently asked react interview hooks solutions from the internet.

Main content

- imperative handle example - frequently asked to react hooks example



Open source projects to borrow from

reboottime commented 1 month ago

Imperative Handle Example

Anti-React pattern: exposes a component's internal state/functions to its parent.


import React, { useImperativeHandle, useRef, useState } from "react";

type CounterRefType = {
  reset: () => void;
  increment: () => void;
  decrement: () => void;
};

const Counter = React.forwardRef<CounterRefType>((props, ref) => {
  const [count, setCount] = useState(0);

  const increment = () => setCount((c) => c + 1);
  const decrement = () => setCount((c) => c - 1);
  const reset = () => setCount(0);

  useImperativeHandle(ref, () => ({
    increment,
    decrement,
    reset,
  }));

  return (
    <div>
      <h3>{count}</h3>
      <p>
        <button onClick={increment}>increment</button>{" "}
        <button onClick={decrement}>decrement</button>
      </p>
    </div>
  );
});

const CounterExample = () => {
  const counterRef = useRef<CounterRefType>(null);

  return (
    <div>
      <Counter ref={counterRef} />
      <button onClick={() => counterRef.current?.reset()}>Reset</button>
    </div>
  );
};

export default CounterExample;
reboottime commented 1 month ago

Frequently asked hooks


import { useCallback, useEffect, useRef, useState } from 'react';

const useUpdateEffect = (callback: CallableFunction) => {
  const firstRender = useRef(true);

  useEffect(() => {
    if (firstRender.current) {
      firstRender.current = false;
      return;
    }
    callback();
  }, [callback]);
};

const useClickOutside = (callback: CallableFunction) => {
  const elemRef = useRef<HTMLElement>(null);

  useEffect(() => {
    const handleClickOutside = (e: MouseEvent) => {
      if (elemRef.current && !elemRef.current.contains(e.target as Node)) {
        callback();
      }
    };

    document.addEventListener('click', handleClickOutside);

    return () => {
      document.removeEventListener('click', handleClickOutside);
    };
  }, [callback]);

  return elemRef;
};

const useIsMounted = () => {
  const isMounted = useRef(false);

  useEffect(() => {
    isMounted.current = true;
    return () => {
      isMounted.current = false;
    };
  }, []);

  return isMounted.current;
};

const useEffectOnce = (callback: CallableFunction) => {
  const firstRender = useRef(true);

  useEffect(() => {
    if (firstRender.current) {
      firstRender.current = false;
      callback();
    }
  }, []);
};

/**
 *
 * @param value First value, useRef(value) sets ref.current as value
 * After the useEffect called, it saves the value of the previous render
 * The next time when calling this func, value is the current value, yet ref.current is the previous value
 */

function usePrevious<T>(value: T): T | undefined {
  const ref = useRef<T>();

  useEffect(() => {
    ref.current = value;
  }, [value]);

  return ref.current;
}

const useHover = <T = HTMLDivElement>() => {
  const [isHovered, setIsHovered] = useState(false);
  const elemRef = useRef<T>(null);

  useEffect(() => {
    const elem = elemRef.current as HTMLDivElement;

    if (!elem) {
      return;
    }
    /**
     * 
     *  Mouse events 
     *  click: Triggered when an element is clicked (i.e., when the mouse button is pressed and released on the same element).
        dblclick: Triggered when an element is double-clicked.
        mousedown: Triggered when a mouse button is pressed down on an element.
        mouseup: Triggered when a mouse button is released over an element.
        mousemove: Triggered when the mouse pointer moves over an element.
        mouseover: Triggered when the mouse pointer enters an element or any of its child elements.
        mouseout: Triggered when the mouse pointer leaves an element or any of its child elements.
        mouseenter: Triggered when the mouse pointer enters an element. It does not bubble up the DOM tree.
        mouseleave: Triggered when the mouse pointer leaves an element. It does not bubble up the DOM tree.
        contextmenu: Triggered when the right mouse button is clicked on an element, typically used to open a context menu.
        wheel: Triggered when the mouse wheel is rotated over an element.

        These mouse events allow you to respond to various mouse interactions and perform specific actions based on those interactions. You can attach event listeners to elements using the addEventListener() method to handle these events.
        For example:
        javascriptCopy codeelement.addEventListener('click', function() {
          // Code to be executed when the element is clicked
        });
        In addition to these events, there are also some related event properties that provide additional information about the mouse interaction:

        clientX and clientY: The X and Y coordinates of the mouse pointer relative to the browser window.
        pageX and pageY: The X and Y coordinates of the mouse pointer relative to the entire document.
        screenX and screenY: The X and Y coordinates of the mouse pointer relative to the user's screen.
        altKey, ctrlKey, shiftKey, and metaKey: Boolean properties indicating whether the corresponding modifier keys (Alt, Ctrl, Shift, or Meta) were pressed during the event.
        button: Indicates which mouse button was pressed (0 for left button, 1 for middle button, 2 for right button).

        These event properties can be accessed within the event listener function to get more details about the mouse interaction.
        Understanding and utilizing these mouse events allows you to create interactive experiences, respond to user actions, and build dynamic user interfaces in JavaScript. CopyRetryClaude does not have the ability to run the code it generates yet.Claude can make mistakes. Please double-check responses.
    */

    const onMouseEnter = () => setIsHovered(true);
    const onMouseLeave = () => setIsHovered(false);

    elem.addEventListener('mouseenter', onMouseEnter);
    elem.addEventListener('mouseleave', onMouseLeave);

    return () => {
      elem?.removeEventListener('mouseenter', onMouseEnter);
      elem?.removeEventListener('mouseleave', onMouseLeave);
    };
  }, []);

  return {
    ref: elemRef,
    isHovered,
  };
};

const useTimeout = (callback: CallableFunction, delay: number) => {
  const timeoutIdRef = useRef<number>();

  const clear = useCallback(() => clearTimeout(timeoutIdRef.current), []);

  useEffect(() => {
    timeoutIdRef.current = setTimeout(callback, delay);

    return () => {
      clearTimeout(timeoutIdRef.current);
    };
  }, [callback, delay]);

  return clear;
};

const useDebounce = <T>(value:T, delay = 500) => {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const handler = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

    return () => {
      clearTimeout(handler);
    };
  }, [value]);

  return debouncedValue;
}

export {
  useDebounce,
  useTimeout,
  useHover,
  useUpdateEffect,
  useClickOutside,
  useIsMounted,
  useEffectOnce,
  usePrevious,
};