@ihabdevteam/core 0.9.4 → 0.9.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { useEffect } from 'react';
4
+ import { useLatestRef } from '../hooks/useLatestRef';
4
5
  import './SplashLogo.css';
5
6
  /**
6
7
  * 앱에 처음 들어올 때 로고를 한 번 보여 주는 연출.
@@ -22,11 +23,20 @@ import './SplashLogo.css';
22
23
  * @public
23
24
  */
24
25
  export default function SplashLogo({ logo, alt, onDone, durationMs = 3000, }) {
26
+ // onDone 을 의존성에 그대로 두면 **다시 그릴 때마다 타이머가 새로 걸린다.** 부모가
27
+ // durationMs 보다 자주 다시 그리면 스플래시가 영영 안 끝난다 — 자료를 받아오는 동안
28
+ // 앱 뿌리가 다시 그리는 것은 흔한 일이고, 그게 바로 스플래시가 떠 있는 때다.
29
+ // 실측: 인라인 화살표로 다섯 번 다시 그리니 새 타이머 5개·취소 5회. 같은 콜백이면 0.
30
+ //
31
+ // 게다가 이 파일 위 예시가 `onDone={() => setShowSplash(false)}` 다 — 권하는 꼴이
32
+ // 곧 덫에 걸리는 꼴이었다. 그래서 부르는 쪽에 안정된 콜백을 요구하는 대신 여기서
33
+ // 담는다. 타이머는 한 번만 걸고, 울릴 때 **그때의 최신 콜백**을 부른다.
34
+ const onDoneRef = useLatestRef(onDone);
25
35
  useEffect(() => {
26
- if (!onDone)
36
+ if (!onDoneRef.current)
27
37
  return;
28
- const timer = setTimeout(onDone, durationMs);
38
+ const timer = setTimeout(() => onDoneRef.current?.(), durationMs);
29
39
  return () => clearTimeout(timer);
30
- }, [onDone, durationMs]);
40
+ }, [durationMs, onDoneRef]);
31
41
  return (_jsx("div", { className: "splash-logo", style: { ['--splash-logo-duration']: `${durationMs}ms` }, "aria-hidden": "true", children: _jsx("img", { src: logo, alt: alt, className: "splash-logo__image" }) }));
32
42
  }
@@ -8,7 +8,15 @@ export interface ImgStats {
8
8
  }
9
9
  /**
10
10
  * 단어쌍 인덱스와 이미지 캐시 현황을 읽어 반환하는 훅.
11
- * `words` 의존성이 변경될 때마다 1초 뒤 갱신 (비동기 빌드 완료 대기).
11
+ * 낱말 목록이 바뀔 때마다, 그리고 1초 뒤 번 더 갱신한다(비동기 빌드 완료 대기).
12
+ *
13
+ * `words` 는 **바뀌었다는 신호로만** 쓴다 — effect 안에서 내용을 읽지 않는다. 통계는
14
+ * 모듈 레벨 인덱스에서 나온다. 그래서 길이를 신호로 삼는다. 참조로 삼으면 안 되는데,
15
+ * 통계를 담는 setState 가 매번 새 객체라 리렌더가 나고, 인라인 배열을 넘긴 호출부에서는
16
+ * 그 리렌더가 다시 새 배열을 만들어 끝나지 않는다(실측: 힙이 터졌다).
17
+ *
18
+ * 길이가 그대로인 채 내용만 통째로 바뀌는 경우는 이 신호로 못 잡는다. 그때는 돌려주는
19
+ * `refreshStats()` 를 직접 부르면 된다.
12
20
  */
13
21
  /**
14
22
  * @public
@@ -4,7 +4,15 @@ import { getPairIndex } from '../utils/wordPairIndex';
4
4
  import { getImageExistenceStats } from '../utils/imageConfig';
5
5
  /**
6
6
  * 단어쌍 인덱스와 이미지 캐시 현황을 읽어 반환하는 훅.
7
- * `words` 의존성이 변경될 때마다 1초 뒤 갱신 (비동기 빌드 완료 대기).
7
+ * 낱말 목록이 바뀔 때마다, 그리고 1초 뒤 번 더 갱신한다(비동기 빌드 완료 대기).
8
+ *
9
+ * `words` 는 **바뀌었다는 신호로만** 쓴다 — effect 안에서 내용을 읽지 않는다. 통계는
10
+ * 모듈 레벨 인덱스에서 나온다. 그래서 길이를 신호로 삼는다. 참조로 삼으면 안 되는데,
11
+ * 통계를 담는 setState 가 매번 새 객체라 리렌더가 나고, 인라인 배열을 넘긴 호출부에서는
12
+ * 그 리렌더가 다시 새 배열을 만들어 끝나지 않는다(실측: 힙이 터졌다).
13
+ *
14
+ * 길이가 그대로인 채 내용만 통째로 바뀌는 경우는 이 신호로 못 잡는다. 그때는 돌려주는
15
+ * `refreshStats()` 를 직접 부르면 된다.
8
16
  */
9
17
  /**
10
18
  * @public
@@ -34,7 +42,7 @@ export function useWordDataStats(words) {
34
42
  refreshStats();
35
43
  const t = setTimeout(refreshStats, 1000);
36
44
  return () => clearTimeout(t);
37
- }, [words, refreshStats]);
45
+ }, [words?.length, refreshStats]);
38
46
  /* eslint-enable react-hooks/set-state-in-effect */
39
47
  return { pairStats, imgStats, setPairStats, setImgStats, refreshStats };
40
48
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ihabdevteam/core",
3
- "version": "0.9.4",
3
+ "version": "0.9.6",
4
4
  "private": false,
5
5
  "main": "dist/ihabdevteam-core.cjs.js",
6
6
  "module": "dist/ihabdevteam-core.esm.js",
@@ -101,6 +101,7 @@
101
101
  "prepare": "npm run build",
102
102
  "prepublishOnly": "npm run build && npm run audit:exports && npm pack --dry-run > /dev/null",
103
103
  "dev": "vite serve playground --config playground/vite.config.ts",
104
+ "typecheck:playground": "tsc -p playground/tsconfig.json --noEmit",
104
105
  "build-playground": "vite build playground --config playground/vite.config.js",
105
106
  "preview-playground": "vite preview --outDir playground/dist",
106
107
  "smoke-test": "node ./scripts/smoke-test.js",