useReducer ) 새로운 상태관리 Hooks
이때까지는 useState로 상태를 만들고 상태 값을 업데이트해 주는 함수를 이용해서 상태를 관리했었다.
useState는 설정하고 싶은 상태를 직접 설정하고 지정해서 상태를 업데이트해 준다.
useReducer는 액션 객체를 기반으로 상태를 업데이트해 준다.
Dispatch --> Reducer --> state
Dispatch는 state에 바로 접근할 수 없고 dispatch함수를 호출하고 dispatch에 연결된 reducer함수를 호출하고 action객체를 이용해서 state값에 접근할 수 있다.
전송
Dispatch(action) --------> Reducer( state , action )
원래 있던 state에서 action이 호출한 값을 받아서 업데이트해 준다.
const [ number , dispatch ] = useReducer( 함수, 초기값 ) --> return [ state, dispatch( fn )]
number에 초기값이 담기고 ,
dispatch에 상태를 변경할 수 있는 함수(Reducer)에 접근하는 함수(dispatch)가 담기게 된다.
저번에 만든 TodoList에서는 useState로 상태값을 만들어서썻는데 이번에는 useReducer로 만들어보겠다.
App.js (useState사용)
mport './App.css';
import Todoheader from './Todoheader';
import Todolists from './Todolist';
import { useState } from 'react';
function App() {
const [todoState,setTodostate] = useState({
todoLists:[],
inputText:""
})
const Change = (e)=>{
setTodostate({
...todoState, //원래있던 todostate값을 저장
inputText: e.target.value
});
};
const Addlist=()=>{
const newtodolist = [
...todoState.todoLists,
{id:todoState.todoLists.length,text:todoState.inputText}
]
setTodostate({
todoLists:newtodolist,
inputText:""
})
}
const delTodoLists = (id)=>{
const newtodolist = todoState.todoLists.filter(list=>list.id !== id);
setTodostate({
...todoState,
todoLists:newtodolist
})
}
return (
<div className="App">
<Todoheader
inputText={todoState.inputText} //value로 들어갈값
Change ={Change}
Addlist={Addlist}
/>
<Todolists
todoLists = {todoState.todoLists} // li를 만들기위해 리스트값을 받아옴
deltodolists = {delTodoLists}
/>
</div>
);
}
export default App;
Todoheader.js
import React from 'react'
const Todoheader = ({inputText,Change,Addlist})=>{
return(
<div className='header'>
<h2>To do List</h2>
<div>
<input value={inputText} onChange={Change}/>
<button onClick={Addlist}>+</button>
</div>
</div>
)
}
export default Todoheader
Todolists.js
import React from 'react'
const Todoheader = ({inputText,Change,Addlist})=>{
return(
<div className='header'>
<h2>To do List</h2>
<div>
<input value={inputText} onChange={Change}/>
<button onClick={Addlist}>+</button>
</div>
</div>
)
}
export default Todoheader
여기까지는 전에 했던 todolist의 코드이다. 이제 여기서 useState로 만들어줬던 상태값을 useReducer로 만들고 상태변환 함수도 reducer라는 이름의 함수로 만들어줘야 한다.
**상태변환함수의 이름은 reducer가 아니어도 상관없다 편의에 따라 붙일 수 있는 함수의 이름이다
App.js (useReducer사용)
import "./styles.css";
import Header from "./Header";
import Todolist from "./Todolist";
import { useReducer } from "react";
//초기값
const initialState = {
todoLists: [],
inputText: ""
};
//상태 변환함수 action객체를 받아서 조건에따라 상태를 변경
function reducer(state, action) {
switch (action.type) {
case "changeInput":
return {
...state,
inputText: action.payload
};
case "addTodo":
return {
...state,
todoLists: [...state.todoLists, action.todo],
inputText: ""
};
case "deleteTodo":
return {
...state,
todoLists: state.todoLists.filter((list) => list.id !== action.id)
};
case "toggleTodo":
return {
...state,
todoLists: state.todoLists.map((todo) =>
todo.id === action.id ? { ...todo, isDone: !todo.isDOne } : todo
)
};
default:
return state; //조건에 일치하는게 없을 경우 원래의 상태값을 반환 변화 X
}
}
export default function App() {
// 상태선언
const [state, dispatch] = useReducer(reducer, initialState); //상태변화함수 , 초기값값
//상태값구조분해 할당
const { todoLists, inputText } = state;
const Change = (e) => {
dispatch({
type: "changeInput",
payload: e.target.value
});
};
const Addlist = () => {
dispatch({
type: "addTodo",
todo: { id: todoLists.length, text: inputText, isDone: false }
});
};
const deltodolists = (id) => {
dispatch({
type: "deleteTodo",
id: id
});
};
const toggleTodo = (id) => {
dispatch({
type: "toggleTodo",
id: id
});
};
return (
<div className="App">
<Header inputText={inputText} Change={Change} Addlist={Addlist} />
<Todolist
todoLists={todoLists}
deltodolists={deltodolists}
toggleTodo={toggleTodo}
/>
</div>
);
}
여기서는 우선 App컴포넌트 위에 상태값으로 사용할 초기값을 변수로 만들어주고 상태변환함수로 사용할 reducer 도 만들어줬다 reducer는 dispatch에서 보내주는 action 객체를 받아서 상태값을 return 해준다. action 객체 의 type을 스위치문의 조건으로 사용해서 조건에 맞을 때 상태를 넣어준다.
컴포넌트에서 상태값을 만들 때 useState처럼 구조분해할당으로 상태값과 상태변환함수를 받아주고
useReducer(reducer함수, 초기값 )을 넣어준다.
// 상태선언
const [state, dispatch] = useReducer(reducer, initialState); //상태변화함수 , 초기값값
//상태값구조분해 할당
const { todoLists, inputText } = state;
그리고 아래에서는 Header와 Todolist에 Props 로 보내줄 값과 함수를 만들어서 보내준다.
함수는 dispatch함수를 이용해서 필요한 값이 들어간 객체를 리턴 시켜주면 reducer 에서 받아서 상태값을 변화시켜 준다.
useReducer를 사용하면 상태를 더 관리하기 쉽고 보기 편하다고 했는데 지금은 그런지 전혀 모르겠다. 나중에 ContextAPI와 같이 사용하면 조금 더 깔끔하게 사용이 가능하다..
다음은 ContextAPI를 이용해서 dispatch를 보내주고 각 컴포넌트에서 받아서 쓸 수 있게 해 주겠다
그러면 app에서 함수를 일일이 만들어서 props로 보내지 않아도
각 컴포넌트에서 dispatch로 reducer함수를 쓸 수 있고 상태값을 바꿀 수 있게 된다.
컴포넌트 바깥 부분에 Context를 만들어주고.↓
export const UserDispatch = createContext();
Context태그로 감싸주고 값으로 dispatch함수를 넣어준다
이제 태그내부의 컴포넌트에서 useContext 를 써서 dispatch함수를 사용할 수 있다.↓
return (
<UserDispatch.Provider value={dispatch}>
<div className="App">
<Header inputText={inputText} id={id} />
<Todolist todoLists={todoLists} />
</div>
</UserDispatch.Provider>
);
Header.js
import React, { useContext } from "react";
import { UserDispatch } from "./App"; //Context가져오기
const Header = ({ inputText, id }) => {
const dispatch = useContext(UserDispatch); //useContext를 써서 Context값 사용 dispatch함수가 담김
return (
<div className="header">
<h2>To do List Reducer</h2>
<div>
<input
value={inputText}
onChange={(e) => {
dispatch({
type: "changeInput",
payload: e.target.value
});
}}
/>
<button
onClick={() => {
dispatch({
type: "addTodo",
todo: { id: id, text: inputText, isDone: false }
});
}}
>
+
</button>
</div>
</div>
);
};
export default Header;
이런 방법으로 원래는 함수를 App컴포넌트에서 만들어서 보내줬었지만 dispatch함수를 각 컴포넌트에서 사용할 수 있게 되면 컴포넌트에서 dispatch를 사용해서 상태를 변경시킬 수 있다.
App.js (ContextAPI로 dispatch 보내기)
import "./styles.css";
import Header from "./Header";
import Todolist from "./Todolist";
import { createContext, useReducer } from "react";
//초기값
const initialState = {
todoLists: [],
inputText: "",
id: 1
};
//상태 변환함수 action객체를 받아서 조건에따라 상태를 변경
function reducer(state, action) {
switch (action.type) {
case "changeInput":
return {
...state,
inputText: action.payload
};
case "addTodo":
return {
...state,
todoLists: [...state.todoLists, action.todo],
inputText: ""
};
case "deleteTodo":
return {
...state,
todoLists: state.todoLists.filter((list) => list.id !== action.id)
};
case "toggleTodo":
return {
...state,
todoLists: state.todoLists.map((todo) =>
todo.id === action.id ? { ...todo, isDone: !todo.isDOne } : todo
)
};
default:
return state; //조건에 일치하는게 없을 경우 원래의 상태값을 반환 변화 X
}
}
//context만들기
export const UserDispatch = createContext();
export default function App() {
// 상태선언
const [state, dispatch] = useReducer(reducer, initialState); //상태변화함수 , 초기값값
//상태값구조분해 할당
const { todoLists, inputText, id } = state;
// props로 보내주던 함수들 이제는 context로 각 컴포넌트에서 dispatch를 사용해서 만들어서보내주지않아도된다
// const Change = (e) => {
// dispatch({
// type: "changeInput",
// payload: e.target.value
// });
// };
// const Addlist = () => {
// dispatch({
// type: "addTodo",
// todo: { id: todoLists.length, text: inputText, isDone: false }
// });
// };
// const deltodolists = (id) => {
// dispatch({
// type: "deleteTodo",
// id: id
// });
// };
// const toggleTodo = (id) => {
// dispatch({
// type: "toggleTodo",
// id: id
// });
// };
return (
<UserDispatch.Provider value={dispatch}>
<div className="App">
<Header inputText={inputText} id={id} />
<Todolist todoLists={todoLists} />
</div>
</UserDispatch.Provider>
);
}
Header.js
import React, { useContext } from "react";
import { UserDispatch } from "./App";
const Header = ({ inputText, id }) => {
const dispatch = useContext(UserDispatch);
return (
<div className="header">
<h2>To do List Reducer</h2>
<div>
<input
value={inputText}
onChange={(e) => {
dispatch({
type: "changeInput",
payload: e.target.value
});
}}
/>
<button
onClick={() => {
dispatch({
type: "addTodo",
todo: { id: id, text: inputText, isDone: false }
});
}}
>
+
</button>
</div>
</div>
);
};
export default Header;
Header컴포넌트에서 useContext로 dispatch함수를 받아와서 reducer함수에 접근해서
상태값을 바꿀 수 있다.
Todolist.js
import React, { useContext } from "react";
import { UserDispatch } from "./App";
const Todolist = ({ todoLists }) => {
const dispatch = useContext(UserDispatch);
return (
<div>
<ul>
{todoLists.map((list) => (
<li key={list.id} style={{ color: list.isDone ? "#eee" : "#333" }}>
<span
onClick={() => {
dispatch({
type: "toggleTodo",
id: list.id
});
}}
>
{list.text}
</span>
<button
onClick={() =>
dispatch({
type: "deleteTodo",
id: list.id
})
}
>
삭제
</button>
</li>
))}
</ul>
</div>
);
};
export default Todolist;
아직은 좀 익숙치 않기는 한데 몇 번 더쓰고나면 useState만큼 편하게 쓸수있지않을까., 많은 상태값을 한번에 관리 하기는 좋은것같다.
'프론트엔드 정복기 > 리액트' 카테고리의 다른 글
[React]19 아이콘 컴포넌트 (react-icons) (0) | 2023.01.18 |
---|---|
[React]18 (axios) (0) | 2023.01.18 |
[React]16 성능최적화2 (useCallback) (0) | 2023.01.16 |
[React]15 리액트 성능 최적화(useMemo) (0) | 2023.01.12 |
[React]14 styled-component part2 (2) | 2023.01.12 |