@albatro33/sy-funcs
v1.1.0
Published
커링 기반 React 상태 업데이트 + Validation 훅 라이브러리. immutable.js 스타일의 편리함을 순수 JS 또는 Immer로 제공합니다.
Maintainers
Readme
sy-funcs
커링 기반 React 상태 업데이트 + Validation 훅 라이브러리
React에서 깊은 객체/배열의 상태를 쉽게 업데이트할 수 있는 커링 기반 훅 라이브러리입니다.
immutable.js의 setIn과 같은 편리함을 순수 JavaScript 또는 Immer로 제공합니다.
설치
npm install sy-funcs
# or
yarn add sy-funcs참고: Immer 버전의 훅을 사용하려면
immer를 별도로 설치해야 합니다.
npm install immer주요 기능
- ✅ 커링 문법: 함수 재사용성이 뛰어난 커링 패턴
- ✅ 깊은 경로 지원: 중첩된 객체/배열을 쉽게 업데이트
- ✅ 순수 JS & Immer: 순수 JavaScript 또는 Immer 중 선택 가능
- ✅ 이벤트 자동 처리: React 이벤트 객체 자동 변환
- ✅ Validation 내장: 경로 기반 폼 검증 지원
- ✅ TypeScript 지원: 완전한 타입 정의 제공
사용법
기본 예제
import { useState } from 'react';
import { useChange } from 'sy-funcs';
function UserForm() {
const [state, setState] = useState({
user: {
name: '',
profile: {
age: 0,
email: ''
}
}
});
const handleChange = useChange(setState);
return (
<div>
<input
value={state.user.name}
onChange={handleChange(['user', 'name'])}
/>
<input
type="number"
value={state.user.profile.age}
onChange={handleChange(['user', 'profile', 'age'])}
/>
<input
value={state.user.profile.email}
onChange={handleChange(['user', 'profile', 'email'])}
/>
</div>
);
}배열 업데이트
import { useState } from 'react';
import { useChange, useUpdate } from 'sy-funcs';
function TodoList() {
const [state, setState] = useState({
todos: [
{ id: 1, text: '할일 1', done: false },
{ id: 2, text: '할일 2', done: false }
]
});
const handleChange = useChange(setState);
const handleUpdate = useUpdate(setState);
return (
<div>
{state.todos.map((todo, index) => (
<div key={todo.id}>
<input
type="checkbox"
checked={todo.done}
onChange={handleChange(['todos', index, 'done'])}
/>
<input
value={todo.text}
onChange={handleChange(['todos', index, 'text'])}
/>
</div>
))}
<button
onClick={handleUpdate(['todos'])((prev) => [
...prev,
{ id: Date.now(), text: '새 할일', done: false }
])}
>
추가
</button>
</div>
);
}Immer 사용
import { useState } from 'react';
import { useChangeImmer } from 'sy-funcs';
function App() {
const [state, setState] = useState({
users: [
{ name: 'John', age: 25 },
{ name: 'Jane', age: 30 }
]
});
// Immer를 사용하면 내부적으로 더 효율적으로 업데이트됩니다
const handleChange = useChangeImmer(setState);
return (
<input
value={state.users[0].name}
onChange={handleChange(['users', 0, 'name'])}
/>
);
}직접 값 전달
function App() {
const [state, setState] = useState({ count: 0 });
const handleChange = useChange(setState);
return (
<button onClick={() => handleChange(['count'])(10)}>
Set to 10
</button>
);
}값 업데이트 (이전 값 기반)
import { useState } from 'react';
import { useUpdate } from 'sy-funcs';
function Counter() {
const [state, setState] = useState({ count: 0 });
const handleUpdate = useUpdate(setState);
return (
<div>
<button onClick={() => handleUpdate(['count'])((n) => n + 1)}>
증가
</button>
<button onClick={() => handleUpdate(['count'])((n) => n - 1)}>
감소
</button>
<p>Count: {state.count}</p>
</div>
);
}Validation + Auto Focus
import { useState } from 'react';
import { useValidatedChange, validators } from 'sy-funcs';
function SignupForm() {
const [state, setState] = useState({ email: '', password: '' });
const {
handleChange,
handleBlur,
getError,
fieldRef, // ref 함수
validateAll,
isValid
} = useValidatedChange(
state,
setState,
{
validation: {
rules: {
'email': [validators.required(), validators.email()],
'password': [validators.required(), validators.minLength(8)],
}
},
validateOn: 'blur',
autoFocus: true, // 자동 focus 활성화
}
);
const handleSubmit = () => {
if (!validateAll?.()) {
// 자동으로 첫 번째 에러 필드에 focus!
return;
}
alert('가입 완료!');
};
return (
<form>
<input
ref={fieldRef?.(['email'])} {/* ref 추가 */}
value={state.email}
onChange={handleChange(['email'])}
onBlur={handleBlur?.(['email'])}
/>
{getError?.(['email']) && <span>{getError(['email'])}</span>}
<input
ref={fieldRef?.(['password'])} {/* ref 추가 */}
type="password"
value={state.password}
onChange={handleChange(['password'])}
onBlur={handleBlur?.(['password'])}
/>
{getError?.(['password']) && <span>{getError(['password'])}</span>}
<button onClick={handleSubmit} disabled={!isValid}>가입</button>
</form>
);
}API
Hooks
useChange(setter, options?)
순수 JavaScript로 깊은 경로의 값을 업데이트하는 커링 훅
파라미터:
setter: setState 함수options.valueExtractor: 커스텀 값 추출 함수options.setInOptions.createMissing: 중간 경로 자동 생성 여부 (기본: true)
반환값: (path: Path) => (eventOrValue: any) => void
useChangeImmer(setter, options?)
Immer를 사용하여 깊은 경로의 값을 업데이트하는 커링 훅
useUpdate(setter, options?)
순수 JavaScript로 이전 값을 기반으로 업데이트하는 커링 훅
반환값: (path: Path) => (updater: (prev) => newValue) => void
useUpdateImmer(setter, options?)
Immer를 사용하여 이전 값을 기반으로 업데이트하는 커링 훅
Utility Functions
setIn(obj, path, value, options?)
순수 JavaScript로 깊은 경로에 값을 설정 (불변성 유지)
const obj = { user: { name: 'John' } };
const newObj = setIn(obj, ['user', 'name'], 'Jane');
// { user: { name: 'Jane' } }setInImmer(obj, path, value, options?)
Immer를 사용하여 깊은 경로에 값을 설정
getIn(obj, path, fallback?)
깊은 경로에서 값을 가져오기
const obj = { user: { name: 'John' } };
getIn(obj, ['user', 'name']); // 'John'
getIn(obj, ['user', 'age'], 0); // 0 (fallback)updateIn(obj, path, updater, defaultValue?, options?)
순수 JavaScript로 깊은 경로의 값을 업데이트
const obj = { count: 5 };
const newObj = updateIn(obj, ['count'], (n) => n + 1);
// { count: 6 }updateInImmer(obj, path, updater, defaultValue?, options?)
Immer를 사용하여 깊은 경로의 값을 업데이트
타입
type PathKey = string | number;
type Path = readonly PathKey[];
type ValueExtractor = (eventOrValue: any) => any;
interface SetInOptions {
createMissing?: boolean;
}
type Setter<T> = (updater: T | ((prev: T) => T)) => void;왜 sy-funcs인가?
Before (without sy-funcs)
// 😰 복잡하고 반복적인 코드
const handleNameChange = (e) => {
setState(prev => ({
...prev,
user: {
...prev.user,
profile: {
...prev.user.profile,
name: e.target.value
}
}
}));
};
const handleAgeChange = (e) => {
setState(prev => ({
...prev,
user: {
...prev.user,
profile: {
...prev.user.profile,
age: Number(e.target.value)
}
}
}));
};After (with sy-funcs)
// 😍 간결하고 재사용 가능한 코드
const handleChange = useChange(setState);
<input onChange={handleChange(['user', 'profile', 'name'])} />
<input onChange={handleChange(['user', 'profile', 'age'])} />비교
| 기능 | sy-funcs | immer + useState | 순수 JS | |------|----------|------------------|---------| | 커링 문법 | ✅ | ❌ | ❌ | | 코드 간결성 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐ | | 함수 재사용성 | ⭐⭐⭐⭐⭐ | ⭐⭐ | ⭐ | | 깊은 경로 업데이트 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐ | | 이벤트 자동 처리 | ✅ | ❌ | ❌ |
라이선스
MIT
기여
이슈와 PR은 언제나 환영합니다!
