@ihabdevteam/core 0.9.6 → 0.9.7

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.
@@ -61,31 +61,34 @@ export default function TrainingStepConfirm({ index, stepIndex, isLast = false,
61
61
  /* 여기 오면 늘 active 다 — canNotify 는 아래 흐름을 그대로 두기 위해 남긴다. */
62
62
  const canNotify = currentState === 'active' || currentState === 'done';
63
63
  if (multiSelect) {
64
- setSelectedSet((prev) => {
65
- const next = new Set(prev);
66
- const wasSelected = next.has(val);
67
- if (wasSelected)
68
- next.delete(val);
69
- else
70
- next.add(val);
71
- if (typeof onChange === 'function') {
72
- onChange({ selectedValues: [...next], __ts: Date.now() });
73
- }
74
- if (canNotify) {
75
- // correctCount가 지정된 문제는 그 개수를 다 채워야 제출된다 — 지정 안 됐을 때만
76
- // (예: 정답이 정해지지 않은 단일 선택형) 아무거나 하나 고르면 바로 제출한다.
77
- // 이전엔 "|| !wasSelected"로 묶여 있어 correctCount 지정 여부와 무관하게
78
- // 추가 선택마다 항상 true가 되어, 목표 개수를 채우기 전에도 매번 제출됐다.
79
- const hasCorrectCount = typeof correctCount === 'number' && correctCount > 0;
80
- const shouldSubmitNow = hasCorrectCount ? next.size >= correctCount : !wasSelected;
81
- if (shouldSubmitNow && typeof onDone === 'function') {
82
- if (currentState === 'active') {
83
- onDone({ selectedValues: [...next], __ts: Date.now() });
84
- }
64
+ /* 다음 선택 집합을 **updater 밖에서** 만든다. 예전에는 updater 안에서 만들면서
65
+ 거기서 onChange/onDone 까지 불렀는데, updater 는 순수해야 하고 React 가 렌더
66
+ 단계에 부를 있다 — 그래서 "렌더 중에 다른 컴포넌트를 갱신한다" 는 경고가
67
+ 났다(실측). 클릭은 discrete event 라 처리 시점에 상태가 이미 커밋돼 있으므로
68
+ 여기서 selectedSet 을 읽어도 어긋나지 않는다. */
69
+ const next = new Set(selectedSet);
70
+ const wasSelected = next.has(val);
71
+ if (wasSelected)
72
+ next.delete(val);
73
+ else
74
+ next.add(val);
75
+ setSelectedSet(next);
76
+ if (typeof onChange === 'function') {
77
+ onChange({ selectedValues: [...next], __ts: Date.now() });
78
+ }
79
+ if (canNotify) {
80
+ // correctCount가 지정된 문제는 개수를 채워야 제출된다 — 지정 안 됐을 때만
81
+ // (예: 정답이 정해지지 않은 단일 선택형) 아무거나 하나 고르면 바로 제출한다.
82
+ // 이전엔 "|| !wasSelected"로 묶여 있어 correctCount 지정 여부와 무관하게
83
+ // 추가 선택마다 항상 true가 되어, 목표 개수를 채우기 전에도 매번 제출됐다.
84
+ const hasCorrectCount = typeof correctCount === 'number' && correctCount > 0;
85
+ const shouldSubmitNow = hasCorrectCount ? next.size >= correctCount : !wasSelected;
86
+ if (shouldSubmitNow && typeof onDone === 'function') {
87
+ if (currentState === 'active') {
88
+ onDone({ selectedValues: [...next], __ts: Date.now() });
85
89
  }
86
90
  }
87
- return next;
88
- });
91
+ }
89
92
  }
90
93
  else {
91
94
  setSelectedAnswer(val);
@@ -98,20 +101,17 @@ export default function TrainingStepConfirm({ index, stepIndex, isLast = false,
98
101
  }
99
102
  }
100
103
  }
101
- }, [choices, currentState, multiSelect, onChange, onDone, correctCount]);
102
- useEffect(() => {
103
- if (!multiSelect || selectedSet.size === 0)
104
- return;
105
- if (typeof onChange === 'function') {
106
- onChange({ selectedValues: [...selectedSet], __ts: Date.now() });
107
- }
108
- if (typeof correctCount === 'number' && correctCount > 0) {
109
- if (selectedSet.size >= correctCount && currentState === 'active') {
110
- if (typeof onDone === 'function')
111
- onDone({ selectedValues: [...selectedSet], __ts: Date.now() });
112
- }
113
- }
114
- }, [selectedSet, multiSelect, onChange, correctCount, currentState, onDone]);
104
+ }, [choices, currentState, multiSelect, onChange, onDone, correctCount, selectedSet]);
105
+ /* 여기 있던 effect 는 selectedSet 이 바뀔 때마다 onChange/onDone 을 **한 번 더**
106
+ 불렀다. 클릭 핸들러가 이미 같은 일을 하고 있어 한 번 고를 때마다 두 번 보고됐다.
107
+ 게다가 콜백이 그 effect 의 의존성에 있어서, 부르는 쪽이 인라인 화살표를 주고
108
+ 받은 payload 를 담으면(payload Date.now() 가 있어 담는 값이 매번 다르다)
109
+ onChange 부모 리렌더 새 화살표 → effect → onChange … 로 끝나지 않았다.
110
+ 실측: 고르기 한 번에 40회(세운 상한). 상한을 빼면 10분이 지나도 안 끝났다.
111
+
112
+ 지울 수 있는 까닭은 selectedSet 실제로 채우는 곳이 클릭 핸들러 하나뿐이기
113
+ 때문이다 — 나머지 둘(choices 바뀜·페이지 전환)은 빈 Set 으로 되돌리는 자리라
114
+ effect `size === 0` 가드에 걸려 어차피 아무것도 안 했다. */
115
115
  const btnsClassName = `training-step__buttons${Array.isArray(choices) && choices.length > 3 ? ' training-step__buttons--wrap-3' : ''}`;
116
116
  return (_jsx(TrainingStep, { index: index, stepIndex: stepIndex, isLast: isLast, title: resolvedTitle, useTitleFaded: useTitleFaded, children: _jsx("div", { className: btnsClassName, children: Array.isArray(choices) && choices.length > 0 ? (choices.map((c, i) => (_jsx(Button, { size: 56, type: "bar", onClick: () => handleSelect(i), ariaLabel: String(c), label: c, useLabel: true, icon: choiceIcons ? choiceIcons[String(c)] : undefined, variant: 'default', disabled: currentState === 'disabled', selected: multiSelect ? selectedSet.has(c) : selectedAnswer === c, useDisabledCovered: useDisabledCovered, minWidth: 0 }, String(c))))) : null }) }));
117
117
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ihabdevteam/core",
3
- "version": "0.9.6",
3
+ "version": "0.9.7",
4
4
  "private": false,
5
5
  "main": "dist/ihabdevteam-core.cjs.js",
6
6
  "module": "dist/ihabdevteam-core.esm.js",