varparts=[‘shoulders’,‘knees’];varlyrics=[‘head’,…parts,‘and’,‘toes’];// [“head”, “shoulders”, “knees”, “and”, “toes”]vararr=[1,2,3];vararr2=[…arr];// arr.slice() 와 유사arr2.push(4);// arr2 은 [1, 2, 3, 4] 이 됨// arr 은 영향을 받지 않고 남아 있음
// 액션 타입 정의하기constCHANGE_INPUT='todos/CHANGE_INPUT';// 인풋 값을 변경함constINSERT='todos/INSERT';// 새로운 todo 를 등록함constTOGGLE='todos/TOGGLE';// todo 를 체크/체크해제 함constREMOVE='todos/REMOVE';// todo 를 제거함// 액션 생성 함수 만들기exportconstchangeInput=input=>({type:CHANGE_INPUT,input});letid=3;// insert 가 호출 될 때마다 1씩 더해집니다.exportconstinsert=text=>({type:INSERT,todo:{id:id++,text,done:false,}});exportconsttoggle=id=>({type:TOGGLE,id});exportconstremove=id=>({type:REMOVE,id});// 초기 상태 및 리듀서 함수 만들기constinitialState={input:'',todos:[{id:1,text:'리덕스 기초 배우기',done:true,},{id:2,text:'리액트와 리덕스 사용하기',done:false,},],};functiontodos(state=initialState,action){switch(action.type){caseCHANGE_INPUT:return{...state,input:action.input};caseINSERT:return{...state,todos:state.todos.concat(action.todo)};caseTOGGLE:return{...state,todos:state.todos.map(todo=>todo.id===action.id?{...todo,done:!todo.done}:todo)};caseREMOVE:return{...state,todos:state.todos.filter(todo=>todo.id!==action.id)};default:returnstate;}}exportdefaulttodos;
mapDispatchToProps: 액션 생성 함수를 컴포넌트의 props로 넘겨주는 함수
containers/TodosContainer.js
import{connect}from'react-redux';import{changeInput,insert,toggle,remove}from'../modules/todos';importTodosfrom'../components/Todos';constTodosContainer=({input,todos,changeInput,insert,toggle,remove,})=>{return(<Todosinput={input}todos={todos}onChangeInput={onChangeInput}onInsert={onInsert}onToggle={onToggle}onRemove={onRemove}/>
);};exportdefaultconnect(// 비구조화 할당을 통해 todos를 분리하여// state.todos.input 대신 todos.input을 사용({todos})=>({input:todos.input,todos:todos.todos,}),{changeInput,insert,toggle,remove,},)(TodosContainer);
mapDispatchToProps를 함수 형태로 넣지 않고 액션 생성 함수의 객체 형태로 넣을 수 있음
components/Todos.js
importReactfrom'react';constTodoItem=({todo,onToggle,onRemove})=>{return(<div><inputtype="checkbox"onClick={()=>onToggle(todo.id)}checked={todo.done}readOnly={true}/>
<spanstyle=>{todo.text}</span>
<buttononClick={()=>onRemove(todo.id)}>삭제</button>
</div>
);};constTodos=({input,// 인풋에 입력되는 텍스트todos,// 할 일 목록이 들어있는 객체onChangeInput,onInsert,onToggle,onRemove,})=>{constonSubmit=e=>{e.preventDefault();onInsert(input);onChangeInput('');// 등록 후 인풋 초기화};constonChange=e=>onChangeInput(e.target.value);return(<div><formonSubmit={onSubmit}><inputvalue={input}onChange={onChange}/>
<buttontype="submit">등록</button>
</form>
<div>{todos.map(todo=>(<TodoItemtodo={todo}key={todo.id}onToggle={onToggle}onRemove={onRemove}/>
))}</div>
</div>
);};exportdefaultTodos;
리덕스 더 편하게 사용하기
redux-actions
createAction으로 액션 생성 함수 선언 가능
액션에 필요한 추가 데이터는 payload라는 이름을 사용
switch/case 문이 아닌 hadleActions 함수를 사용
immer
객체의 불변성을 쉽게 유지할 수 있음
produce(originalState, draft => function) 형태로 사용
originalState: 수정하고 싶은 상태
draft => function: 상태를 어떻게 업데이트할 지 정의하는 함수
modules/todos.js
import{createAction,handleActions}from'redux-actions';importproducefrom'immer';constCHANGE_INPUT='todos/CHANGE_INPUT';// 인풋 값을 변경함constINSERT='todos/INSERT';// 새로운 todo 를 등록함constTOGGLE='todos/TOGGLE';// todo 를 체크/체크해제 함constREMOVE='todos/REMOVE';// todo 를 제거함exportconstchangeInput=createAction(CHANGE_INPUT,input=>input);letid=3;// insert 가 호출 될 때마다 1씩 더해집니다.exportconstinsert=createAction(INSERT,text=>({id:id++,text,done:false,}));exportconsttoggle=createAction(TOGGLE,id=>id);exportconstremove=createAction(REMOVE,id=>id);constinitialState={input:'',todos:[{id:1,text:'리덕스 기초 배우기',done:true,},{id:2,text:'리액트와 리덕스 사용하기',done:false,},],};consttodos=handleActions({[CHANGE_INPUT]:(state,{payload:input})=>produce(state,draft=>{draft.input=input;}),[INSERT]:(state,{payload:todo})=>produce(state,draft=>{draft.todos.push(todo);}),[TOGGLE]:(state,{payload:id})=>produce(state,draft=>{consttodo=draft.todos.find(todo=>todo.id===id);todo.done=!todo.done;}),[REMOVE]:(state,{payload:id})=>produce(state,draft=>{constindex=draft.todos.findIndex(todo=>todo.id===id);draft.todos.splice(index,1);}),},initialState,);exportdefaulttodos;
Hooks를 사용하여 컨테이너 컴포넌트 만들기
useSelector로 상태 조회하기
useSelector를 사용하여 connect 함수를 사용하지 않고 리덕스의 상태를 조회할 수 있음
const result = useSelector(상태 선택 함수)
useDispatch를 사용하여 액션 디스패치하기
useDispatch를 사용하여 컴포넌트 내부에서 스토어의 내장 함수인 dispatch를 사용할 수 있음
useCallback으로 액션을 디스패치하는 함수를 감싸 컴포넌트 성능을 최적화
useStore를 사용하여 리덕스 스토어 사용하기
useStore를 사용하여 컴포넌트 내부에서 리덕스 스토어 객체를 직접 사용할 수 있음
스토어 객체에 직접 접근하는 대신 useSelector를 사용하는 것을 권장
useActions 유틸 Hook을 만들어서 사용하기
useActions를 사용하여 여러 개의 액션을 사용할 때 코드를 깔끔하게 정리할 수 있음
리덕스에서 제공하지는 않으나 공식 문서에서 복사하여 사용할 수 있음
참고 링크: https://react-redux.js.org/api/hooks#recipe-useactions