@@ -31,54 +31,49 @@ describe(useLocalStorage, () => {
3131 } ) ;
3232
3333 describe ( "基本動作" , ( ) => {
34- it ( "初期値を返すこと " , ( ) => {
34+ it ( "未設定なら null を返す " , ( ) => {
3535 const key = uniqueKey ( ) ;
36- const { result } = renderHook ( ( ) => useLocalStorage ( key , "default" ) ) ;
37- expect ( result . current [ 0 ] ) . toBe ( "default" ) ;
36+ const { result } = renderHook ( ( ) => useLocalStorage ( key ) ) ;
37+ expect ( result . current [ 0 ] ) . toBeNull ( ) ;
3838 } ) ;
3939
40- it ( "localStorageの既存値を読み取ること " , ( ) => {
40+ it ( "localStorageの既存値を読み取る " , ( ) => {
4141 const key = uniqueKey ( ) ;
42- mockStorage . setItem ( key , JSON . stringify ( "stored" ) ) ;
43- const { result } = renderHook ( ( ) => useLocalStorage ( key , "default" ) ) ;
42+ mockStorage . setItem ( key , "stored" ) ;
43+ const { result } = renderHook ( ( ) => useLocalStorage ( key ) ) ;
4444 expect ( result . current [ 0 ] ) . toBe ( "stored" ) ;
4545 } ) ;
4646
47- it ( "setValueで値を更新できること " , ( ) => {
47+ it ( "setValueで値を更新できる " , ( ) => {
4848 const key = uniqueKey ( ) ;
49- const { result } = renderHook ( ( ) => useLocalStorage ( key , "default" ) ) ;
49+ const { result } = renderHook ( ( ) => useLocalStorage ( key ) ) ;
5050
5151 act ( ( ) => {
5252 result . current [ 1 ] ( "updated" ) ;
5353 } ) ;
5454
5555 expect ( result . current [ 0 ] ) . toBe ( "updated" ) ;
56- expect ( mockStorage . setItem ) . toHaveBeenCalledWith (
57- key ,
58- JSON . stringify ( "updated" ) ,
59- ) ;
56+ expect ( mockStorage . setItem ) . toHaveBeenCalledWith ( key , "updated" ) ;
6057 } ) ;
6158
62- it ( "関数型更新をサポートすること " , ( ) => {
59+ it ( "関数型更新をサポートする(prev は string | null) " , ( ) => {
6360 const key = uniqueKey ( ) ;
64- const { result } = renderHook ( ( ) => useLocalStorage ( key , 0 ) ) ;
61+ const { result } = renderHook ( ( ) => useLocalStorage ( key ) ) ;
6562
6663 act ( ( ) => {
67- result . current [ 1 ] ( ( prev ) => prev + 1 ) ;
64+ result . current [ 1 ] ( ( prev ) => ` ${ prev ?? "" } a` ) ;
6865 } ) ;
69-
70- expect ( result . current [ 0 ] ) . toBe ( 1 ) ;
66+ expect ( result . current [ 0 ] ) . toBe ( "a" ) ;
7167
7268 act ( ( ) => {
73- result . current [ 1 ] ( ( prev ) => prev + 10 ) ;
69+ result . current [ 1 ] ( ( prev ) => ` ${ prev ?? "" } b` ) ;
7470 } ) ;
75-
76- expect ( result . current [ 0 ] ) . toBe ( 11 ) ;
71+ expect ( result . current [ 0 ] ) . toBe ( "ab" ) ;
7772 } ) ;
7873
79- it ( "removeValueで値を削除して初期値に戻ること " , ( ) => {
74+ it ( "removeValue 後は null に戻る " , ( ) => {
8075 const key = uniqueKey ( ) ;
81- const { result } = renderHook ( ( ) => useLocalStorage ( key , "default" ) ) ;
76+ const { result } = renderHook ( ( ) => useLocalStorage ( key ) ) ;
8277
8378 act ( ( ) => {
8479 result . current [ 1 ] ( "stored" ) ;
@@ -89,92 +84,26 @@ describe(useLocalStorage, () => {
8984 result . current [ 2 ] ( ) ;
9085 } ) ;
9186
92- expect ( result . current [ 0 ] ) . toBe ( "default" ) ;
93- expect ( mockStorage . removeItem ) . toHaveBeenCalledWith ( key ) ;
94- } ) ;
95- } ) ;
96-
97- describe ( "シリアライズ" , ( ) => {
98- it ( "オブジェクトを正しく保存・読み取りできること" , ( ) => {
99- const key = uniqueKey ( ) ;
100- const obj = { name : "test" , count : 42 } ;
101- const { result } = renderHook ( ( ) =>
102- useLocalStorage ( key , { name : "" , count : 0 } ) ,
103- ) ;
104-
105- act ( ( ) => {
106- result . current [ 1 ] ( obj ) ;
107- } ) ;
108-
109- expect ( result . current [ 0 ] ) . toStrictEqual ( obj ) ;
110- } ) ;
111-
112- it ( "配列を正しく保存・読み取りできること" , ( ) => {
113- const key = uniqueKey ( ) ;
114- const arr = [ 1 , 2 , 3 ] ;
115- const { result } = renderHook ( ( ) => useLocalStorage < number [ ] > ( key , [ ] ) ) ;
116-
117- act ( ( ) => {
118- result . current [ 1 ] ( arr ) ;
119- } ) ;
120-
121- expect ( result . current [ 0 ] ) . toStrictEqual ( arr ) ;
122- } ) ;
123-
124- it ( "数値を正しく処理すること" , ( ) => {
125- const key = uniqueKey ( ) ;
126- const { result } = renderHook ( ( ) => useLocalStorage ( key , 0 ) ) ;
127-
128- act ( ( ) => {
129- result . current [ 1 ] ( 42 ) ;
130- } ) ;
131-
132- expect ( result . current [ 0 ] ) . toBe ( 42 ) ;
133- } ) ;
134-
135- it ( "nullを正しく処理すること" , ( ) => {
136- const key = uniqueKey ( ) ;
137- const { result } = renderHook ( ( ) =>
138- useLocalStorage < string | null > ( key , null ) ,
139- ) ;
140-
141- expect ( result . current [ 0 ] ) . toBeNull ( ) ;
142-
143- act ( ( ) => {
144- result . current [ 1 ] ( "value" ) ;
145- } ) ;
146- expect ( result . current [ 0 ] ) . toBe ( "value" ) ;
147-
148- act ( ( ) => {
149- result . current [ 1 ] ( null ) ;
150- } ) ;
15187 expect ( result . current [ 0 ] ) . toBeNull ( ) ;
88+ expect ( mockStorage . removeItem ) . toHaveBeenCalledWith ( key ) ;
15289 } ) ;
15390 } ) ;
15491
15592 describe ( "エラーハンドリング" , ( ) => {
156- it ( "localStorage利用不可時に初期値を返すこと " , ( ) => {
93+ it ( "localStorage 利用不可時に null を返す " , ( ) => {
15794 const key = uniqueKey ( ) ;
15895 mockStorage . getItem . mockImplementation ( ( ) => {
15996 throw new Error ( "localStorage unavailable" ) ;
16097 } ) ;
16198
162- const { result } = renderHook ( ( ) => useLocalStorage ( key , "fallback" ) ) ;
163- expect ( result . current [ 0 ] ) . toBe ( "fallback" ) ;
164- } ) ;
165-
166- it ( "破損JSONの場合に初期値を返すこと" , ( ) => {
167- const key = uniqueKey ( ) ;
168- mockStorage . setItem ( key , "invalid-json{{{" ) ;
169-
170- const { result } = renderHook ( ( ) => useLocalStorage ( key , "fallback" ) ) ;
171- expect ( result . current [ 0 ] ) . toBe ( "fallback" ) ;
99+ const { result } = renderHook ( ( ) => useLocalStorage ( key ) ) ;
100+ expect ( result . current [ 0 ] ) . toBeNull ( ) ;
172101 } ) ;
173102
174- it ( "QuotaExceededError時にconsole.warnを出力し状態を変更しないこと " , ( ) => {
103+ it ( "QuotaExceededError 時に console.warn を出力し状態を変更しない " , ( ) => {
175104 const key = uniqueKey ( ) ;
176105 const warnSpy = vi . spyOn ( console , "warn" ) . mockImplementation ( ( ) => { } ) ;
177- const { result } = renderHook ( ( ) => useLocalStorage ( key , "default" ) ) ;
106+ const { result } = renderHook ( ( ) => useLocalStorage ( key ) ) ;
178107
179108 mockStorage . setItem . mockImplementation ( ( ) => {
180109 throw new DOMException ( "quota exceeded" , "QuotaExceededError" ) ;
@@ -185,32 +114,33 @@ describe(useLocalStorage, () => {
185114 } ) ;
186115
187116 expect ( warnSpy ) . toHaveBeenCalledTimes ( 1 ) ;
188- expect ( result . current [ 0 ] ) . toBe ( "default" ) ;
189- // localStorage に書き込まれていないことを確認
117+ expect ( result . current [ 0 ] ) . toBeNull ( ) ;
190118 expect ( mockStorage . getItem ( key ) ) . toBeNull ( ) ;
191119 } ) ;
192120 } ) ;
193121
194122 describe ( "タブ間同期" , ( ) => {
195- it ( "storageイベントで値が更新されること " , ( ) => {
123+ it ( "storage イベントで値が更新される " , ( ) => {
196124 const key = uniqueKey ( ) ;
197- const { result } = renderHook ( ( ) => useLocalStorage ( key , "default" ) ) ;
125+ const { result } = renderHook ( ( ) => useLocalStorage ( key ) ) ;
198126
199127 act ( ( ) => {
128+ mockStorage . setItem ( key , "from-other-tab" ) ;
200129 window . dispatchEvent (
201130 new StorageEvent ( "storage" , {
202131 key,
203- newValue : JSON . stringify ( "from-other-tab" ) ,
132+ newValue : "from-other-tab" ,
133+ storageArea : window . localStorage ,
204134 } ) ,
205135 ) ;
206136 } ) ;
207137
208138 expect ( result . current [ 0 ] ) . toBe ( "from-other-tab" ) ;
209139 } ) ;
210140
211- it ( "storageイベントで削除を検知すること " , ( ) => {
141+ it ( "storage イベントで削除を検知する " , ( ) => {
212142 const key = uniqueKey ( ) ;
213- const { result } = renderHook ( ( ) => useLocalStorage ( key , "default" ) ) ;
143+ const { result } = renderHook ( ( ) => useLocalStorage ( key ) ) ;
214144
215145 act ( ( ) => {
216146 result . current [ 1 ] ( "stored" ) ;
@@ -223,39 +153,37 @@ describe(useLocalStorage, () => {
223153 new StorageEvent ( "storage" , {
224154 key,
225155 newValue : null ,
156+ storageArea : window . localStorage ,
226157 } ) ,
227158 ) ;
228159 } ) ;
229160
230- expect ( result . current [ 0 ] ) . toBe ( "default" ) ;
161+ expect ( result . current [ 0 ] ) . toBeNull ( ) ;
231162 } ) ;
232163
233- it ( "無関係なキーのstorageイベントを無視すること " , ( ) => {
164+ it ( "無関係なキーの storage イベントを無視する " , ( ) => {
234165 const key = uniqueKey ( ) ;
235- const { result } = renderHook ( ( ) => useLocalStorage ( key , "default" ) ) ;
166+ const { result } = renderHook ( ( ) => useLocalStorage ( key ) ) ;
236167
237168 act ( ( ) => {
238169 window . dispatchEvent (
239170 new StorageEvent ( "storage" , {
240171 key : "other-key" ,
241- newValue : JSON . stringify ( "other-value" ) ,
172+ newValue : "other-value" ,
173+ storageArea : window . localStorage ,
242174 } ) ,
243175 ) ;
244176 } ) ;
245177
246- expect ( result . current [ 0 ] ) . toBe ( "default" ) ;
178+ expect ( result . current [ 0 ] ) . toBeNull ( ) ;
247179 } ) ;
248180 } ) ;
249181
250182 describe ( "同一タブ同期" , ( ) => {
251- it ( "同じキーの複数インスタンスが同期すること " , ( ) => {
183+ it ( "同じキーの複数インスタンスが同期する " , ( ) => {
252184 const key = uniqueKey ( ) ;
253- const { result : result1 } = renderHook ( ( ) =>
254- useLocalStorage ( key , "default" ) ,
255- ) ;
256- const { result : result2 } = renderHook ( ( ) =>
257- useLocalStorage ( key , "default" ) ,
258- ) ;
185+ const { result : result1 } = renderHook ( ( ) => useLocalStorage ( key ) ) ;
186+ const { result : result2 } = renderHook ( ( ) => useLocalStorage ( key ) ) ;
259187
260188 act ( ( ) => {
261189 result1 . current [ 1 ] ( "synced" ) ;
@@ -267,18 +195,29 @@ describe(useLocalStorage, () => {
267195 } ) ;
268196
269197 describe ( "クリーンアップ" , ( ) => {
270- it ( "アンマウント時にリスナーが解除されること " , ( ) => {
198+ it ( "アンマウント後は同タブの書き込みで再レンダーされない " , ( ) => {
271199 const key = uniqueKey ( ) ;
272- const addSpy = vi . spyOn ( window , "addEventListener" ) ;
273- const removeSpy = vi . spyOn ( window , "removeEventListener" ) ;
274-
275- const { unmount } = renderHook ( ( ) => useLocalStorage ( key , "default" ) ) ;
276-
277- expect ( addSpy ) . toHaveBeenCalledWith ( "storage" , expect . any ( Function ) ) ;
200+ const renderSpy = vi . fn ( ) ;
201+ const { unmount } = renderHook ( ( ) => {
202+ renderSpy ( ) ;
203+ return useLocalStorage ( key ) ;
204+ } ) ;
278205
206+ const renderCountBeforeUnmount = renderSpy . mock . calls . length ;
279207 unmount ( ) ;
280208
281- expect ( removeSpy ) . toHaveBeenCalledWith ( "storage" , expect . any ( Function ) ) ;
209+ act ( ( ) => {
210+ window . localStorage . setItem ( key , "after-unmount" ) ;
211+ window . dispatchEvent (
212+ new StorageEvent ( "storage" , {
213+ key,
214+ newValue : "after-unmount" ,
215+ storageArea : window . localStorage ,
216+ } ) ,
217+ ) ;
218+ } ) ;
219+
220+ expect ( renderSpy ) . toHaveBeenCalledTimes ( renderCountBeforeUnmount ) ;
282221 } ) ;
283222 } ) ;
284223} ) ;
0 commit comments