React
React - App 컴포넌트에서 useReducer사용
Starters
2020. 9. 11. 15:20
React - useReducer
이전에는 useState로 상태 업데이트 해줬음 useReducer로도 할 수 있음 useState는 setValue(5); 와 설정하고 싶은 다음 상태를 직접 지정해줘서 업데이트함 useReducer는 action이라는 객체를 기반으로 상태를 �
startersdev.tistory.com
1. App 컴포넌트에서 사용할 초기 상태를 컴포넌트 밖에 선언해 줌
const initialState = {
inputs: {
username: '',
email: '',
},
users: [
{
id: 1,
username: 'starters',
email: 'public.starters@gmail.com',
active: true,
},
{
id: 2,
username: 'tester',
email: 'tester@gmail.com',
active: false,
},
{
id: 3,
username: 'jajajojo',
email: 'jajajojo@gmail.com',
active: false,
}
]
}
2. App컴포넌트에서 사용한 모든 로직 지우기, 내부 props도 지우기
function App() {
return (
<>
<CreateUser
/>
<UserList users={[]}/>
<div>활성 사용자 수: 0</div>
</>
);
}
- users에 임시로 빈 배열 넣고, count는 0으로
3. useState 대신에 useReducer 불러오기
import React, { useRef, useReducer, useMemo, useCallback } from 'react';
4. reducer 틀 잡기
function reducer(state, action){
return state;
}
function App() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
<CreateUser
/>
<UserList users={[]}/>
<div>활성 사용자 수: 0</div>
</>
);
}
5. state 안에 있는 users와 inputs 비구조 할당으로 추출 후, 컴포넌트를 통해 props로 전달
function App() {
const [state, dispatch] = useReducer(reducer, initialState);
const { users } = state;
const { username, email } = state.inputs;
return (
<>
<CreateUser
username={username}
email={email}
/>
<UserList users={users}/>
<div>활성 사용자 수: 0</div>
</>
);
}
6. onChange함수 구현하고 사용
const onChange = useCallback(e => {
const { name, value } = e.target;
dispatch({
type: 'CHANGE_INPUT',
name,
value
});
}, []);
return (
<>
<CreateUser
username={username}
email={email}
onChange={onChange}
/>
...
7. onCreate()
const onCreate = useCallback(() => {
dispatch({
type: 'CREATE_USER',
user: {
id: 1,
username,
email,
}
});
}, [username, email]);
return (
<>
<CreateUser
- id 는 임의로 1, 나중에 useRef로 관리해주어야 함
1) nextId 값 관리
function App() {
const [state, dispatch] = useReducer(reducer, initialState);
const nextId = useRef(4);
const { users } = state;
- App함수 상단에 선언, 초기값은 4 기존에 3개 들어있기 때문
2) create 함수에 넣어줌
const onCreate = useCallback(() => {
dispatch({
type: 'CREATE_USER',
user: {
id: nextId.current,
username,
email,
}
});
nextId.current += 1;
}, [username, email]);
3) reducer 함수에도 추가
function reducer(state, action){
switch (action.type) {
case 'CHANGE_INPUT':
return {
...state,
inputs: {
...state.inputs,
[action.name]: action.value
}
};
case 'CREATE_USER':
return {
inputs: initialState.inputs,
users: state.users.concat(action.user)
}
default:
throw new Error('Unhandled action');
}
}
4) props도 지정
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
-> 이러고 나면 새 글 생성할 수 있음
8. onToggle / onRemove
- reducer 안에 먼저 추가
case 'TOGGLE_USER':
return {
...state,
users: state.users.map(user =>
user.id === action.id
? { ...user, active: !user.active }
: user
)
};
case 'REMOVE_USER':
return {
...state,
users: state.users.filter(user => user.id !== action.id)
};
- 함수 생성, props 추가
const onToggle = useCallback(id => {
dispatch({
type: 'TOGGLE_USER',
id
});
}, []);
const onRemove = useCallback(id => {
dispatch({
type: 'REMOVE_USER',
id
});
}, []);
return (
<>
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users}
onToggle={onToggle}
onRemove={onRemove}/>
<div>활성 사용자 수: 0</div>
</>
);
9. 활성 사용자 수
const count = useMemo(() => countActiveUsers(users), [users]);
return (
<>
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users}
onToggle={onToggle}
onRemove={onRemove}/>
<div>활성 사용자 수: {count}</div>
</>
- 이전과 동일, useMemo 사용하고 props 추가
※ 언제 useReducer 사용하고 useState 사용할 지
- 확실하게 정해진 것은 없음
- 값이 한두개이거나 boolean값이면 useState가 편할 것
- 컴포넌트에서 관리하는 값이 여러개이거나 구조가 복잡하면 useReducer가 나을 수 있음
- setUsers, setInputs 처럼 useState자주 사용되면 useReducer 사용을 고려해보는 것도 좋음
이전 코드
import React, { useRef, useState, useMemo, useCallback } from 'react';
import UserList from './UserList';
import CreateUser from './CreateUser';
function countActiveUsers(users){
console.log('활성 사용자 수를 세는 중...');
return users.filter(user => user.active).length;
}
function App() {
const [inputs, setInputs] = useState({
username: '',
email: ''
});
const {username, email} = inputs;
const onChange = useCallback(e => {
const {name, value} = e.target;
setInputs({
...inputs,
[name]: value
});
}, [inputs]);
const [users, setUsers] = useState([
{
id: 1,
username: 'starters',
email: 'public.starters@gmail.com',
active: true,
},
{
id: 2,
username: 'tester',
email: 'tester@gmail.com',
active: false,
},
{
id: 3,
username: 'jajajojo',
email: 'jajajojo@gmail.com',
active: false,
}
]);
const nextId = useRef(4);
const onCreate = useCallback(() => {
const user = {
id: nextId.current,
username,
email,
};
setUsers(users => users.concat(user));
setInputs({
username: '',
email: ''
});
console.log(nextId.current);
nextId.current += 1;
}, [username, email]);
const onRemove = useCallback(id => {
setUsers(users => users.filter(user => user.id !== id))
}, []);
const onToggle = useCallback(id => {
setUsers(users => users.map(
user => user.id === id
? { ...user, active: !user.active }
: user
));
}, []);
const count = useMemo(() => countActiveUsers(users), [users]);
return (
<>
<CreateUser
username={username}
email={email}
onChange={onChange}
onCreate={onCreate}
/>
<UserList users={users} onRemove={onRemove} onToggle={onToggle}/>
<div>활성 사용자 수: {count}</div>
</>
);
}
export default App;