Skip to content

Commit 75f5fdc

Browse files
committed
main 🧊 rework use object
1 parent cb3d11f commit 75f5fdc

File tree

5 files changed

+212
-161
lines changed

5 files changed

+212
-161
lines changed

‎packages/core/src/bundle/hooks/state.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ export * from './useList/useList';
1212
// storage
1313
export * from './useLocalStorage/useLocalStorage';
1414
export * from './useMap/useMap';
15-
export * from './useObject/useObject';
1615
export * from './useOffsetPagination/useOffsetPagination';
1716
export * from './useQuery/useQuery';
1817
export * from './useQueue/useQueue';

‎packages/core/src/bundle/hooks/useObject/useObject.js

Lines changed: 25 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,49 +4,42 @@ import { useState } from 'react';
44
* @description - Hook that provides state and helper methods to manage an object
55
* @category State
66
*
7-
* @template T The type of the object
8-
* @param {T} initialValue The initial object value
9-
* @returns {UseObjectReturn<T>} An object containing the current state and functions to interact with the object
7+
* @template Value The type of the object
8+
* @param {Value} initialValue The initial object value
9+
* @returns {UseObjectReturn<Value>} An object containing the current state and functions to interact with the object
1010
*
1111
* @example
12-
* const { state, set, get, reset, update, merge, remove } = useObject({ a: 1, b: 2 });
12+
* const { value, set, reset, remove, update, merge, clear, toggle, has, keys, isEmpty, size } = useObject({ name: 'John', age: 30, isActive: true });
1313
*/
1414
export function useObject(initialValue) {
15-
const [state, setState] = useState(initialValue);
16-
const set = (key, value) => {
17-
setState((prev) => ({
18-
...prev,
19-
[key]: value
15+
const [value, setValue] = useState(initialValue);
16+
const set = (value) =>
17+
setValue((prevValue) => ({
18+
...prevValue,
19+
...value
2020
}));
21-
};
22-
const get = (key) => {
23-
return state[key];
24-
};
25-
const reset = () => {
26-
setState(initialValue);
27-
};
28-
const update = (updater) => {
29-
setState((prev) => updater(prev));
30-
};
31-
const merge = (newState) => {
32-
setState((prev) => ({
33-
...prev,
34-
...newState
35-
}));
36-
};
21+
const reset = () => setValue(initialValue);
3722
const remove = (key) => {
38-
setState((prev) => {
39-
const { [key]: _, ...rest } = prev;
23+
if (!(key in value)) return;
24+
setValue((prevValue) => {
25+
const { [key]: _, ...rest } = prevValue;
4026
return rest;
4127
});
4228
};
29+
const clear = () => setValue({});
30+
const has = (key) => key in value;
31+
const keys = Object.keys(value);
32+
const empty = !Object.keys(value).length;
33+
const size = Object.keys(value).length;
4334
return {
44-
state,
35+
value,
4536
set,
46-
get,
4737
reset,
48-
update,
49-
merge,
50-
remove
38+
remove,
39+
clear,
40+
has,
41+
keys,
42+
empty,
43+
size
5144
};
5245
}

‎packages/core/src/hooks/useObject/useObject.demo.tsx

Lines changed: 29 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,42 @@
11
import { useObject } from './useObject';
22

33
const Demo = () => {
4-
const { state, get, set, update, merge, remove, reset } = useObject({
4+
const profileObject = useObject({
55
name: 'Vladislav',
66
age: '32',
77
email: 'testmail@mail.com'
88
});
99

1010
return (
11-
<div className='flex items-start gap-4'>
12-
<div className=''>
13-
<input
14-
type='text'
15-
value={state.name}
16-
onChange={(event) => set('name', event.target.value)}
17-
placeholder='Name'
18-
/>
19-
<input
20-
type='number'
21-
value={state.age}
22-
onChange={(event) => set('age', event.target.value)}
23-
placeholder='Age'
24-
/>
25-
<input
26-
type='email'
27-
value={state.email}
28-
onChange={(event) => set('email', event.target.value)}
29-
placeholder='Email'
30-
/>
31-
<div className=''>
32-
<button onClick={reset}>Reset</button>
33-
<button onClick={() => alert(get('email'))}>Get Email</button>
34-
<button onClick={() => update((prev) => ({ ...prev, age: '10' }))}>Update Age</button>
35-
<button onClick={() => merge({ name: 'Updated name', age: '10' })}>
36-
Merge Name and Age
37-
</button>
38-
<button onClick={() => remove('email')}>Remove Email</button>
39-
</div>
40-
</div>
11+
<div>
12+
<input
13+
type='text'
14+
value={profileObject.value.name}
15+
onChange={(event) => profileObject.set({ name: event.target.value })}
16+
placeholder='Name'
17+
/>
18+
<input
19+
type='number'
20+
value={profileObject.value.age}
21+
onChange={(event) => profileObject.set({ age: event.target.value })}
22+
placeholder='Age'
23+
/>
24+
25+
<button onClick={profileObject.reset}>Reset</button>
26+
27+
<button
28+
onClick={() =>
29+
profileObject.set({
30+
age: `${Number(profileObject.value.age) + 1}`
31+
})
32+
}
33+
>
34+
Update Age
35+
</button>
36+
<button onClick={() => profileObject.remove('email')}>Remove Email</button>
37+
4138
<pre>
42-
<code>{JSON.stringify(state, null, 2)}</code>
39+
<code>{JSON.stringify(profileObject.value, null, 2)}</code>
4340
</pre>
4441
</div>
4542
);

‎packages/core/src/hooks/useObject/useObject.test.ts

Lines changed: 113 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,72 +2,136 @@ import { act, renderHook } from '@testing-library/react';
22

33
import { useObject } from './useObject';
44

5-
const INITIAL_OBJECT = { a: 1, b: 2, c: 3 };
6-
7-
describe('useObject', () => {
8-
it('Should use object', () => {
9-
const { result } = renderHook(() => useObject(INITIAL_OBJECT));
10-
const obj = result.current;
11-
12-
expect(obj.state).toEqual(INITIAL_OBJECT);
13-
expect(obj.set).toBeTypeOf('function');
14-
expect(obj.get).toBeTypeOf('function');
15-
expect(obj.reset).toBeTypeOf('function');
16-
expect(obj.update).toBeTypeOf('function');
17-
expect(obj.merge).toBeTypeOf('function');
18-
expect(obj.remove).toBeTypeOf('function');
19-
});
5+
it('Should use object', () => {
6+
const { result } = renderHook(() => useObject({ a: 1, b: 2, c: 3 }));
7+
8+
expect(result.current.value).toEqual({ a: 1, b: 2, c: 3 });
9+
expect(result.current.set).toBeTypeOf('function');
10+
expect(result.current.reset).toBeTypeOf('function');
11+
expect(result.current.remove).toBeTypeOf('function');
12+
expect(result.current.clear).toBeTypeOf('function');
13+
expect(result.current.has).toBeTypeOf('function');
14+
expect(result.current.keys).toBeTypeOf('object');
15+
expect(result.current.empty).toBeTypeOf('boolean');
16+
expect(result.current.size).toBeTypeOf('number');
17+
});
2018

21-
it('Should set property', () => {
22-
const { result } = renderHook(() => useObject(INITIAL_OBJECT));
23-
const obj = result.current;
19+
it('Should set initial value', () => {
20+
const { result } = renderHook(() => useObject({ name: 'John', age: 30 }));
2421

25-
act(() => obj.set('a', 42));
22+
expect(result.current.value).toEqual({ name: 'John', age: 30 });
23+
});
2624

27-
expect(result.current.state).toEqual({ a: 42, b: 2, c: 3 });
28-
});
25+
it('Should set partial object', () => {
26+
const { result } = renderHook(() => useObject({ a: 1, b: 2, c: 3 }));
2927

30-
it('Should get property', () => {
31-
const { result } = renderHook(() => useObject(INITIAL_OBJECT));
32-
const obj = result.current;
28+
act(() => result.current.set({ a: 42 }));
3329

34-
expect(obj.get('b')).toBe(2);
35-
});
30+
expect(result.current.value).toEqual({ a: 42, b: 2, c: 3 });
31+
});
3632

37-
it('Should reset object', () => {
38-
const { result } = renderHook(() => useObject(INITIAL_OBJECT));
39-
const obj = result.current;
33+
it('Should set multiple properties', () => {
34+
const { result } = renderHook(() => useObject({ a: 1, b: 2, c: 3 }));
4035

41-
act(() => obj.set('a', 99));
42-
act(() => obj.reset());
36+
act(() => result.current.set({ a: 42, c: 99 }));
4337

44-
expect(result.current.state).toEqual(INITIAL_OBJECT);
45-
});
38+
expect(result.current.value).toEqual({ a: 42, b: 2, c: 99 });
39+
});
4640

47-
it('Should update object', () => {
48-
const { result } = renderHook(() => useObject(INITIAL_OBJECT));
49-
const obj = result.current;
41+
it('Should reset object', () => {
42+
const { result } = renderHook(() => useObject({ a: 1, b: 2 }));
5043

51-
act(() => obj.update((prev) => ({ ...prev, d: 4 }) as typeof INITIAL_OBJECT & { d: number }));
44+
act(() => result.current.set({ a: 99 }));
45+
act(() => result.current.reset());
5246

53-
expect(result.current.state).toEqual({ a: 1, b: 2, c: 3, d: 4 });
54-
});
47+
expect(result.current.value).toEqual({ a: 1, b: 2 });
48+
});
49+
50+
it('Should remove property', () => {
51+
const { result } = renderHook(() => useObject({ a: 1, b: 2, c: 3 }));
52+
53+
act(() => result.current.remove('b'));
54+
55+
expect(result.current.value).toEqual({ a: 1, c: 3 });
56+
});
57+
58+
it('Should not remove non-existing property', () => {
59+
const { result } = renderHook(() => useObject({ a: 1, b: 2 }));
60+
61+
act(() => result.current.remove('c' as any));
62+
63+
expect(result.current.value).toEqual({ a: 1, b: 2 });
64+
});
65+
66+
it('Should clear object', () => {
67+
const { result } = renderHook(() => useObject({ a: 1, b: 2, c: 3 }));
68+
69+
act(() => result.current.clear());
70+
71+
expect(result.current.value).toEqual({});
72+
});
73+
74+
it('Should check if property exists', () => {
75+
const { result } = renderHook(() => useObject({ a: 1, b: 2 }));
76+
77+
expect(result.current.has('a')).toBe(true);
78+
expect(result.current.has('b')).toBe(true);
79+
expect(result.current.has('c' as any)).toBe(false);
80+
});
81+
82+
it('Should return object keys', () => {
83+
const { result } = renderHook(() => useObject({ a: 1, b: 2, c: 3 }));
5584

56-
it('Should merge object', () => {
57-
const { result } = renderHook(() => useObject(INITIAL_OBJECT));
58-
const obj = result.current;
85+
expect(result.current.keys).toEqual(['a', 'b', 'c']);
86+
});
87+
88+
it('Should update keys when object changes', () => {
89+
const { result } = renderHook(() => useObject({ a: 1, b: 2, c: 3 }));
90+
91+
expect(result.current.keys).toEqual(['a', 'b', 'c']);
92+
93+
act(() => result.current.remove('b'));
94+
95+
expect(result.current.keys).toEqual(['a', 'c']);
96+
});
97+
98+
it('Should check if object is empty', () => {
99+
const { result } = renderHook(() => useObject({}));
100+
101+
expect(result.current.empty).toBe(true);
102+
103+
act(() => result.current.set({ a: 1 }));
104+
105+
expect(result.current.empty).toBe(false);
106+
});
107+
108+
it('Should return object size', () => {
109+
const { result } = renderHook(() => useObject({ a: 1, b: 2, c: 3 }));
110+
111+
expect(result.current.size).toBe(3);
112+
113+
act(() => result.current.remove('b'));
114+
115+
expect(result.current.size).toBe(2);
116+
});
59117

60-
act(() => obj.merge({ b: 10 }));
118+
describe('Empty object', () => {
119+
it('Should handle empty initial object', () => {
120+
const { result } = renderHook(() => useObject({}));
61121

62-
expect(result.current.state).toEqual({ a: 1, b: 10, c: 3 });
122+
expect(result.current.value).toEqual({});
123+
expect(result.current.empty).toBe(true);
124+
expect(result.current.size).toBe(0);
125+
expect(result.current.keys).toEqual([]);
63126
});
64127

65-
it('Should remove property', () => {
66-
const { result } = renderHook(() => useObject(INITIAL_OBJECT));
67-
const obj = result.current;
128+
it('Should add properties to empty object', () => {
129+
const { result } = renderHook(() => useObject({}));
68130

69-
act(() => obj.remove('b'));
131+
act(() => result.current.set({ a: 1, b: 2 }));
70132

71-
expect(result.current.state).toEqual({ a: 1, c: 3 });
133+
expect(result.current.value).toEqual({ a: 1, b: 2 });
134+
expect(result.current.empty).toBe(false);
135+
expect(result.current.size).toBe(2);
72136
});
73137
});

0 commit comments

Comments
 (0)