컴포넌트에서 상태변화 로직을 분리
const [<상태 객체>, <dispatch 함수>] = useReducer(<reducer 함수>, <초기 상태>, <초기 함수>)
<상태 객체> : 현재 state라고 할 수 있다.
<dispatch 함수> : 상태를 변화시키는 액션을 발생시키는 함수(상태변화를 일으킴)
<reducer 함수> : <dispatch 함수>가 일으킨 상태변화를 처리해주는 역할을 한다. discpatch함수가 호출이 되었을 때 reducer함수가 호출된다.
<초기 상태> : <상태 객체>의 초기값
<button onClick={()=> dispatch({type : 1})}>add 1</ button>
✔ 'add 1'이라는 버튼을 누르면 dispatch함수를 호출하면서 객체를 전달하게 된다.
✔ 그 객체{type : 1}는 꼭 type이라는 프로포티가 들어있다.
✔ dispatch함수가 같이 전달되는 이 객체{type : 1}를 'Action 객체(상태변화를 설명할 객체)'라고 한다.
✔ reducer 함수의 첫번재 인자(state): 상태변화가 일어나기 직전의 state값이다.
예시에서는 초기값인 1을 넣어주면 된다.
✔ reducer 함수의 두번째 인자(action): 어떤 상태변화를 일으켜야 하는지의 대한 정보가 담긴 Action 객체가 담긴다.
예시에서는 dispatch함수의 Action 객체{type : 1}를 넣어준다.
✔ 상태관리를 처리하는 reducer 함수는 switch 케이스문을 활용해서 action의 type에 따라서 각각 다른 값을 반환한다. 반환된 값은 새로운 state 값이 된다.
👀 흐름
//App 컴포넌트
import logo from './logo.svg';
import { useEffect, useMemo, useRef, useReducer} from 'react';
//1. useState를 주석처리하고 import한 것을 지우고 useReducer을 import해온다.
import './App.css';
import DiaryEditor from './DiaryEditor';
import DiaryList from './DiaryList';
//reducer 함수를 만들어주는데 App 컴포넌트에서 분리하기 위해
//만든 것이기에 App컴포넌트 밖에서 만들어 준다.
const reducer = (state, action)=> {
switch(action.type){
//여기서 리턴하는 값은 useReducer의 data값이 된다.
case 'INIT': {
return action.data
// action 객체에서 키가 data인 값의 value를 가져와 리턴해준다.
//dispatch({type : 'INIT', data: initData})
//여기서 action.data의 값은 initData이기에 리턴값은 initData이다.
}
case 'CREATE': {
const create_date = new Date().getTime();
const newItem = {
...action.data,
create_date
}
return [newItem, ...state]
// 일기 리스트에 새로운 일기 데이터를 추가해주고 전에 원래
// 있었던 일기 데이터들을 spread연산자로 다시 넣어준다.
}
case 'REMOVE': {
return state.filter((it)=>it.id !==action.targetId);
//지금 현재 State(일기 데이터 배열)값을 filter메소드를 이용하여
//순회하여 그 데이터의 id가 targetId와 같지 않은 경우에만
//리턴되게 해준다.
}
case 'EDIT': {
return state.map((it)=>
it.id === action.targetId ?
{...it, content : action.newCountent} : it)
//targetId와 똑같은 요소를 찾아준 후 그 요소의 값을
//newContent로 바꿔주고
//나머지 요소들을 spread요소로 다시 돌려준다.
}
default :
return state;
//default 일때는 상태를 변화시키지 않는다.
}
}
function App() {
// const [data, setData]= useState([]);
const [data, dispatch] =useReducer(reducer, [])
//data라는 state에 어떤 값이 필요한가(초기화,추가,제거,수정)
//App컴포넌트에서 set데이터가 했었던 역활을 dispatch와 reducer에 나눠줘야 한다.
const dataId = useRef(0)
//*INIT*/
const getData = async() =>{
const res = await fetch('https://jsonplaceholder.typicode.com/comments')
.then((res) => res.json());
const initData = res.slice(0,20).map((it)=>{
return{
author : it.email,
content : it.body,
emotion : Math.floor(Math.random()*5)+1,
create_date : new Date().getTime(),
id : dataId.current ++
}
})
dispatch({type : 'INIT', data: initData})
//reducer은 action객체를 받는데 그 action의 type이 'INIT'이고
// 그 action에 필요한 데이터는 iniData가 된다.
//setData의 역할을 reducer이 하니깐 아래 코드는 지워도 된다.
// setData(initData);
}
useEffect(() => {
getData()
},[] )
//*CREATE*/
const onCreate =(author,content, emotion)=> {
dispatch({type : 'CREATE', data: {
author,
content,
emotion,
id : dataId.current}})
// const create_date = new Date().getTime()
// const newItem = {
// author,
// content,
// emotion,
// create_date,
// id : dataId.current
// }
dataId.current +=1
// setData([newItem, ...data])
};
//*EDIT*/
const onEdit = (targetId, newCountent) => {
dispatch({type : 'EDIT', targetId, newCountent})
//reducer 함수의 두번쨰 인자인 action에 targetId, newCountent가 전달된다.
// setData(
// data.map((it)=>
// it.id === targetId ?
// {...it, content:newCountent}
// : it)
// )
}
//*REMOVE*/
const onRemove = (targetId) => {
dispatch({type : 'REMOVE', targetId})
// reducer에게 어떤 아이디를 가진 데이터를 지워라는
//의미에서 targetId만 전달한다.
// const newDiaryList = data.filter((it)=>it.id !==targetId);
// setData(newDiaryList);
}
const getDiaryAnalysis = useMemo(() => {
const goodCount = data.filter((it)=> it.emotion >=3).length;
const badCount = data.length - goodCount;
const goodRatio = (goodCount/ data.length) *100;
return{goodCount, badCount, goodRatio};
},[data.length ])
const {goodCount, badCount, goodRatio} = getDiaryAnalysis;
return (
<div className="App">
<DiaryEditor onCreate = {onCreate}/>
<div>전체일기 : {data.length}</div>
<div>기분 좋은 일기 개수 : {goodCount}</div>
<div>기분 나쁜 일기 개수 : {badCount}</div>
<div>기분 좋은 일기 비율 : {goodRatio}</div>
<DiaryList onEdit={onEdit} onRemove={onRemove} diaryList ={data}/>
</div>
);
}
export default App;