리엑트 리덕스로 처음부터 끝까지 투두리스트 만드는법을
기록하는 포스팅이다.
간단하게만 작성을 해보겠다.
순서를 어떻게 넣어야 될까 하다가...
그냥 전체 코드에 설명 넣는식이 가장 베스트일것 같아서 그렇게 해보겠다.
yarn create react-app 이름
yarn add redux react-redux
yarn add styled-components
yarn add react-router-dom
yarn add uuid
(import {v4 as uuidv4} from 'uuid';
Id: uuidv4(),)
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Detail from '../pages/Detail';
import Main from '../pages/Main';
import Header from '../redux/components/Header';
// yarn add react-router-dom 설치먼저 해주자
// 위에 import 한것들 잘 보기 빼먹으면 안된다.
const Router = () => {
return(
//라우터 사용할겠다!
<BrowserRouter>
<Header />
{/* 페이지 경로 하나로 묶어주는거 */}
<Routes>
<Route path="/" element={<Main />}></Route>
<Route path="/:id" element={<Detail />}></Route>
</Routes>
</BrowserRouter>
)
}
export default Router;
import {v4 as uuidv4} from 'uuid';
// 1. action items (휴면에러 방지하기 위해 상수로씀 즉 대문자)
const ADD_TODO = "ADD_TODO";
const REMOVE_TODO = "REMOVE_TODO";
const SWITCH_TODO = "SWITCH_TODO";
// 2. action creators
export const addTodo = (payload) => {
return {
type: ADD_TODO,
payload, //payload:payload 줄인거임.
}
}
export const removeTodo = (payload) => {
return {
type: REMOVE_TODO,
payload,
}
}
export const switchTodo = (payload) => {
return {
type: SWITCH_TODO,
payload,
}
}
//에드투두 실행되면 타입 에드투도 실행되고 페이로드 실행되는거
// 3. initial State => reducer를 구성할 때
const initinalState = [
{
id:uuidv4(),
title:'제목1',
contents: '내용1',
isDone: false,
},
{
id:uuidv4(),
title:'제목2',
contents: '내용2',
isDone: true,
},
{
id:uuidv4(),
title:'제목3',
contents: '내용3',
isDone: false,
},
]
//4. reducer를 만들 것
// 어떤 타입의 엑션이(에드투두, 리무브투두, 스위치투두) 들어올지 모르니까 스위치를 써준다. if 사용해도됨.
//Reducer -> state(date)와, 그 state를 다루는(변경하는) 방법을 가지고 있는 '함수'
// action -> 객체 : type / payload
const todos = (state=initinalState, action) => {
switch (action.type) {
case ADD_TODO:
return [...state, action.payload];
case REMOVE_TODO:
return state.filter((item)=> item.id !== action.payload);
case SWITCH_TODO:
return state.map((item) => {
if(item.id === action.payload){
return {...item, isDone: !item.isDone}
} else {
return item;
}
});
default:
return state;
}
}
//5. reducer를 export
export default todos;
import { combineReducers, createStore } from 'redux';
import todos from '../modules/todos';
// Store -> state와 action이 존재한다.
// config : 구성하다.
// store: 우리가 아는 그 store
// 우리가 아는 그 store를 구성하는 파일
//거실이라고 생각하면 된다.
// 1. rootReducer를 만들 것이고 --> reducer들을 합칠 거야
// Reducer : state와 action으로 이루어진 store의 구성조각
//modules 밑에 있는 여러 파일들이 반환하는 값
// 현재: todos, counter, users 있는데 이거 입력당시 아직 안만듬.
const rootReducer = combineReducers({
todos,
// counter,
// users,
});
// 2. 이걸 이용해서 store를 만들 것이다(main)
const store = createStore(rootReducer);
//3. export해서 다른 파일에서 import 할 수 있도록 한다.
export default store;
import React from 'react'
import styled from 'styled-components';
import { useState } from 'react';
import { useDispatch } from 'react-redux';
import { addTodo } from '../modules/todos';
import {v4 as uuidv4} from 'uuid';
function Input() {
const [title, setTitle] =useState("");
const [contents, setContents] = useState("");
const dispatch = useDispatch();
const handleSubmitButtonClick =(event) => {
event.preventDefault();
const newTodo = {
id:uuidv4(),
title,
contents,
isDone: false,
}
dispatch(addTodo(newTodo));
// dispatch({
// type: "ADD_TODO",
// payload: newTodo,
// });
}
const handleTitleInputChange = (event) => {
setTitle(event.target.value);
}
const handleContentsInputChange = (event) => {
setContents(event.target.value);
}
return (
<div>
<StyledInputBox>
<form onSubmit={handleSubmitButtonClick}>
<input onChange={handleTitleInputChange} value={title} type="text" />
<input onChange={handleContentsInputChange} value={contents} type="text" />
<button type="submit">추가</button>
</form>
</StyledInputBox>
</div>
)
}
export default Input
const StyledInputBox = styled.div`
background-color: aqua;
padding: 20px;
`
import React from 'react'
import { useDispatch, useSelector } from 'react-redux';
import { useNavigate } from 'react-router-dom';
import styled from 'styled-components';
import { removeTodo, switchTodo } from '../modules/todos';
function TodoList({isActive}) {
const navigate = useNavigate();
//스토어에 있는 투두스를 가지고 온다
const todos = useSelector((state)=> state.todos);
//이렇게하면 이니셜스테이트값들 나옴
console.log("todos",todos);
const dispatch = useDispatch();
const handleDeleteButtonOnClick = (id) =>{
dispatch(removeTodo(id));
};
const handleSwitchButtonClick = (id) => {
dispatch(switchTodo(id))
}
return (
<div>
<StyledListBox>
<h4>{isActive ? '해야할 일': '완료된 일'}</h4>
{
todos
.filter((item)=> item.isDone === !isActive)
//밑에는 일단 다 불러와주는거고 위에서 필터를 해준다.
.map(item => {
return (
<StyledTodoBox key={item.id}>
<h4>{item.title}</h4>
<p>{item.contents}</p>
<button onClick={()=> handleSwitchButtonClick(item.id)}>{isActive ? '완료':'취소'}</button>
<button onClick={()=> handleDeleteButtonOnClick(item.id)}>삭제</button>
<br />
<br />
<button onClick={()=>{
navigate(`/${item.id}`)
}}>상세보기</button>
</StyledTodoBox>
)
})
}
</StyledListBox>
</div>
)
}
export default TodoList;
const StyledListBox = styled.div`
background-color: beige;
padding: 20px;
`
const StyledTodoBox = styled.div`
background-color: brown;
color: white;
padding: 10px;
margin: 5px;
`
<Provider store={store}>
<APP />
</Provider>
사실 리덕스는 위에 움짤만 보면 끝이다.
필요한것들은 단 3개
중앙판. 즉 거실이라고 생각하면 된다.