フロントエンド開発者のための刺激的なプロジェクト10選　1選目考察【前編】

下記の記事をみてフロントエンドエンジニアになりたい気持ちが強くなったので、1選ずつコード見ていき素人ながらに分析しました。かいつまんで、こんな書き方してるんだーみたいな発見を書いていくだけなので、体系的な説明にはなっていないと思うので悪しからず。お願いします。

https://qiita.com/baby-degu/items/e720c2007716811c8b30

#Object.keys
オブジェクトがもっているキー名を配列として返す。
このプロジェクトではサーバーにおいているSVGへのパスをオブジェクトにまとめてあり、それを`Object.keys`を用いて、img要素を作るために利用している。

```JS

const CONSTANTS = {
  assetPath: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/184729",
}

const ASSETS = {
  head: `${CONSTANTS.assetPath}/head.svg`,
  waiting: `${CONSTANTS.assetPath}/hand.svg`,
  stalking: `${CONSTANTS.assetPath}/hand-waiting.svg`,
  grabbing: `${CONSTANTS.assetPath}/hand.svg`,
  grabbed: `${CONSTANTS.assetPath}/hand-with-cursor.svg`,
  shaka: `${CONSTANTS.assetPath}/hand-surfs-up.svg`
}

// Preload images
Object.keys(ASSETS).forEach(key => {
  const img = new Image();
  img.src = ASSETS[key];
});
```
けど、`Object.keys`の部分を消しても正常に動作しているっぽいので、何のためにこれをやっているのかは理解できていない。

#カスタムフック　Ref　addEventListener
戻り値としてrefとstate(真偽値を示すhoveredという変数)の2つの値配列を返すuseHoverというカスタムフックを作成している。
GrabZoneという関数コンポーネントでuseHoverを呼び出し、refをouterRef変数,innerRef変数に、stateをouterHovered変数,innerHovered変数にそれぞれ分割代入している。
outerRef変数,innerRef変数がref属性に指定されたReact要素に、useHover関数内で定義されたaddEventListenerが設定され、カーソルがそこにホバーするたびにuseHover関数内のenter関数が呼び出され、カーソルが範囲外に行くことでleave関数が実行される。
それによってsetHoverdが実行されhoverd変数の真偽値が切り替わり、if文の条件分岐により、UIに変化をもたらしている。

```JS
const useHover = () => {
  const ref = useRef();
  const [hovered, setHovered] = useState(false);

  const enter = () => setHovered(true);
  const leave = () => setHovered(false);

  useEffect(
    () => {
      ref.current.addEventListener("mouseenter", enter);
      ref.current.addEventListener("mouseleave", leave);
      return () => {
        ref.current.removeEventListener("mouseenter", enter);
        ref.current.removeEventListener("mouseleave", leave);
      };
    },
    [ref]
  );

  return [ref, hovered];
};

const GrabZone = ({ cursorGrabbed, gameOver, onCursorGrabbed }) => {
  const [outerRef, outerHovered] = useHover();
  const [innerRef, innerHovered] = useHover();
  const [isExtended, setExtendedArm] = useState(false);

  let state = "waiting";
  if (outerHovered) {
    state = "stalking";
  }
  if (innerHovered) {
    state = "grabbing";
  }
  if (cursorGrabbed) {
    state = "grabbed";
  }
  if (gameOver) {
    state = "shaka"
  }
  
  // If state is grabbing for a long time, they're being clever!
  useEffect(() => {
      let timer;
      if (state === "grabbing") {
        timer = setTimeout(() => {
          // Not so clever now, are they?
          setExtendedArm(true);
          timer = null;
        }, 2000);
      }
      return () => {
        setExtendedArm(false);
        if (timer) {
          clearTimeout(timer);
        }
      };
    },
    [state]
  );

  return (
    <div className="grab-zone" ref={outerRef}>
      <div className="grab-zone__debug">
        <strong>Debug info:</strong>
        <p>Current state: {state}</p>
        <p>Extended arm: {isExtended ? "Yes" : "No"}</p>
      </div>
      <div className="grab-zone__danger" ref={innerRef}>
        <Grabber
          state={state}
          gameOver={gameOver}
          extended={isExtended}
          onCursorGrabbed={onCursorGrabbed}
        />
      </div>
    </div>
  );
};
```

#イベントハンドラー
このプロジェクトで扱われているイベントは下記の４つ。
###mousemove
>マウスなどのポインティングデバイスで、カーソルのホットスポットが要素内にある間に動いた時に発行されるイベントです。

ここではwindow内でカーソルが動くというイベントに対して、マウスの位置をプログラムが把握するために使用されている。

###mouseenter
>ポインティングデバイス (通常はマウス) のホットスポットが最初にイベントが発生した要素の中に移動したときに Element に発生します。

###mouseleave
>mouseleave イベントは、ポインティングデバイス (ふつうはマウス) のカーソルが Element 外に移動したときに発行されます。

`mouseenter`と`mouseleave`はある要素内へのカーソルの出入りを検知し、stateの真偽値を更新するために使用されていた。

```JS
const useHover = () => {
  const ref = useRef();
  const [hovered, setHovered] = useState(false);

  const enter = () => setHovered(true);
  const leave = () => setHovered(false);

  useEffect(
    () => {
      ref.current.addEventListener("mouseenter", enter);
      ref.current.addEventListener("mouseleave", leave);
      return () => {
        ref.current.removeEventListener("mouseenter", enter);
        ref.current.removeEventListener("mouseleave", leave);
      };
    },
    [ref]
  );

  return [ref, hovered];
};
```

###resize
windowサイズの変更を検知し、要素の寸法と位置を返す`getBoundingClientRect`を呼び出している。


#後編へつづく
コードみたら何をしているのかは推測がつくのですが、このコードを作り上げる発想がすごいと思いました。
引き続きコード読み解いていきます。

https://qiita.com/nakada-web/items/4029f9c4c31be5aa78ab