Dalej poznajemy useEffect, na nieco znanym już nam przykładzie oraz dalej uczymy się pisać custom hooks. Do dzieła.

Ok, nasz dot:

 return (
    <div style={{
      position: 'absolute',
      backgroundColor: 'pink',
      borderRadius: '50%',
      opacity: 0.6,
      transform: `translate(${position.x}px, ${position.y}px)`,
      pointerEvents: 'none',
      left: -20,
      top: -20,
      width: 40,
      height: 40,
    }} />
  );
}

A teraz góra:

import { useState, useEffect } from 'react';

export default function App() {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  useEffect(() => {
    function handleMove(e) {
      setPosition({ x: e.clientX, y: e.clientY });
    }
    window.addEventListener('pointermove', handleMove);
    return () => {
      window.removeEventListener('pointermove', handleMove);
    };
  }, []);

Jak to działa:

  • Każdy setPosition wywołuje re-render
  • useEffect działa tylko raz, ale wykonuje setPosition
  • jako cleanup jest remove event listener

Ok, teraz napiszemy to sobie, ale jako custom hook:

import { useState, useEffect } from 'react';

export function useWindowListener(eventType, listener) {
  useEffect(() => {
    window.addEventListener(eventType, listener);
    return () => {
      window.removeEventListener(eventType, listener);
    };
  }, [eventType, listener]);
}

Ważne – custom hooki muszą zaczynać się od słówka use, inaczej nie moglibyśmy używać hooków Reacta poza funkcjami-komponentami na ich najwyższym (niezagnieżdżonym w niczmym) poziomie.

Teraz używamy tego hooka:

import { useState } from 'react';
import { useWindowListener } from './useWindowListener.js';

export default function App() {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  useWindowListener('pointermove', (e) => {
    setPosition({ x: e.clientX, y: e.clientY });
  });

  return (
    <div style={{
      position: 'absolute',
      backgroundColor: 'pink',
      borderRadius: '50%',
      opacity: 0.6,
      transform: `translate(${position.x}px, ${position.y}px)`,
      pointerEvents: 'none',
      left: -20,
      top: -20,
      width: 40,
      height: 40,
    }} />
  );
}

Więcej useEffect oraz custom hooks już niedługo.