이번에는 react, redux를 사용한 TodoApp을 만들었다.
CRUD의 연습을 위한 작업이었다.
C : Create (생성)
R : Read (읽기)
U : Update (수정)
D : Delete (삭제)
전문적인 느낌은 아니지만, 기능을 어떻게 구현했는지 하나하나 기록을 해보자.
참고 : 개인적으로 진행된 작업이라 부족한 부분이 있을 것.
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './App';
import store from './store';
ReactDOM.render(
// react에서 store를 접근하기 위한
<Provider store={store}>
<App />
</Provider>,
document.querySelector('#root'),
);
// 이 페이지는 딱히 설명할 건 없는 것 같다.
import React from 'react';
import TodoList from './components/Todolist';
import AddList from './components/AddList';
const App = () => {
return (
<>
<AddList />
<TodoList />
</>
);
};
export default App;
import React from 'react';
import { useSelector } from 'react-redux';
import TodoItem from './TodoItem';
const TodoList = () => {
// state 데이터 불러오기.
const item = useSelector((state) => state.post);
// item을 map으로 ToddItem에 props보내기.
return (
<ul>
{item.map((item) => (
<TodoItem key={item.id} item={item} />
))}
</ul>
);
};
export default TodoList;
import React, { useState, useCallback } from 'react';
import { useDispatch } from 'react-redux';
// action 불러오기.
import { REMOVE_TODO_LIST, CHANGE_TODO_LIST } from '../reducers/index';
const TodoItem = ({ item }) => {
const dispatch = useDispatch();
// 리스트 수정 시 state를 변경하여, 조건 렌더링
const [mode, setMode] = useState(true);
// 수정 시 input 초깃값
// 이렇게 넣어주면, 받아온 데이터를 value값으로 사용되면서,
// 데이터를 직접 수정 할 수 없기 때문에 state로 넣어준다.
const [text, setText] = useState(item.title);
const onChangeText = useCallback(
(e) => {
setText(e.target.value);
},
[text],
);
// mode 변경 -> false (수정 폼 렌더링)
const onClickChange = useCallback(() => {
setMode(false);
}, []);
// mode 변경 -> true (초기 화면 렌더링)
const onCancleChange = useCallback(() => {
setMode(true);
}, []);
// Todo 삭제
const onRemove = useCallback((id) => {
dispatch({
type: REMOVE_TODO_LIST,
payload: id,
});
}, []);
// Todo 수정
const onChange = useCallback((id, text) => {
dispatch({
type: CHANGE_TODO_LIST,
payload: {
id: id,
title: text,
},
});
setMode(true);
}, []);
return (
<li>
{mode ? (
<div>
<p style={{ color: item.done ? 'green' : 'black' }}>{item.title}</p>
// 삭제 시 해당 리스트 id를 파라미터로 보낼 시
// 아래처럼 화살표함수로 감싸준다.
<button onClick={() => onRemove(item.id)}>삭제</button>
<button onClick={onClickChange}>수정</button>
</div>
) : (
<div>
<input value={text} onChange={onChangeText} />
<button onClick={onCancleChange}>취소</button>
<button onClick={() => onChange(item.id, text)}>수정완료</button>
</div>
)}
</li>
);
};
export default TodoItem;
mode에 따른 조건 렌더링
mode true일 경우 : 초기 화면
mode false일 경우 : 수정 화면
위 화면처럼 수정 할 수 있는 input에 해당되는 리스트의 내용이
value값에 들어가 있다.
import React, { useState, useCallback } from 'react';
import { useDispatch } from 'react-redux';
// 이번 작업에서는 shortid 라이브러리를 사용하여, id를 만들었다.
import shortId from 'shortid';
// action 불러오기
import { ADD_TODO_LIST } from '../reducers/index';
const AddList = () => {
const [value, setValue] = useState('');
const dispatch = useDispatch();
const onChangeInput = useCallback(
(e) => {
setValue(e.target.value);
},
[value],
);
const onAddList = useCallback(() => {
if (value === '') { // 추가 시 input이 비어있을 경우
return alert('내용을 입력해주세요.');
}
dispatch({
type: ADD_TODO_LIST,
payload: {
id: shortId.generate(), // shortid 생성
title: value, // 리스트 제목
done: false, // 완료? 값
},
});
setValue(''); // 리스트 추가 후 input 비어있도록 설정
}, [value]);
return (
<div className="add-box">
<input type="text" value={value} onChange={onChangeInput} />
<button onClick={onAddList}>추가</button>
</div>
);
};
export default AddList;
import shortId from 'shortid';
// 불변성을 지키기 위해서 immer 라이브러리를 사용했다.
import produce from 'immer';
export const ADD_TODO_LIST = 'ADD_TODO_LIST';
export const REMOVE_TODO_LIST = 'REMOVE_TODO_LIST';
export const CHANGE_TODO_LIST = 'CHANGE_TODO_LIST';
// 초깃값 셋팅
const initialState = {
post: [
{
id: shortId.generate(),
title: '자바스크립트 공부하기',
done: true,
},
{
id: shortId.generate(),
title: '리액트 공부하기',
done: false,
},
{
id: shortId.generate(),
title: '노드 공부하기',
done: false,
},
],
};
// immer 사용 시 produce로 switch문을 감싸준다.
// 여기서는 state값을 바꾸는 것이 아닌 draft를 통해서 바꿔준다.
const todoReducer = (state = initialState, action) => {
return produce(state, (draft) => {
switch (action.type) {
case ADD_TODO_LIST:
draft.post.unshift(action.payload);
break;
case REMOVE_TODO_LIST:
draft.post = draft.post.filter((v) => v.id !== action.payload);
break;
case CHANGE_TODO_LIST:
// 리스트 id를 조회하여 post에 넣기.
const post = draft.post.find((v) => v.id === action.payload.id);
// 조회된 객체의 title을 변경
post.title = action.payload.title;
break;
default:
return state;
}
});
};
export default todoReducer;
import { createStore } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import reducer from './reducers/index';
const store = createStore(reducer, composeWithDevTools());
export default store;
이렇게 해서 Todo App 코딩 작업은 1차 마무리했다.
이제 CSS(Sass)를 통해서 디자인을 해 볼 예정이다.