리엑트 리덕스로 처음부터 끝까지 투두리스트 만들기

JaeSung Lee·2023년 1월 4일
0
post-thumbnail

리엑트 리덕스로 처음부터 끝까지 투두리스트 만드는법을
기록하는 포스팅이다.
간단하게만 작성을 해보겠다.
순서를 어떻게 넣어야 될까 하다가...
그냥 전체 코드에 설명 넣는식이 가장 베스트일것 같아서 그렇게 해보겠다.

시작 세팅

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(),)

1.라우터 설정

1) shared - Router.js

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;
  • 디테일 페이지 /:id 설정한 이유는 투두리스트 클릭할때 고유 아이디값이 있어서 저렇게 설정함.

2) Pages - 원하는페이지.jsx

3) app.js 수정

2. 리덕스 세팅하기

1) redux - modules - 원하는리덕스이름.js

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;

2) redux - config - configStore.js

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;

3. 나머지 설정들

1) redux - components - Input.jsx

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; 
`

2) redux - components - TodoList.jsx

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;
`

3) index.js 감싸기

<Provider store={store}>
<APP />
</Provider>

정리


사실 리덕스는 위에 움짤만 보면 끝이다.
필요한것들은 단 3개

1. config - configStore

중앙판. 즉 거실이라고 생각하면 된다.

2. modules - 리덕스.js

  • 리듀서들이 있는 방이다.(set State하는 역할)
  • 액션(타입, 페이로드) 만든다 (이 엑션들이 디스패치와 일한다.)

3. 컴포넌트안에 있는 useSelctor, dispatch

  • dispatch - 엑션에 의해 받아온 값들을 스토어에 운반해서 리듀서로 옮겨서 기능 수행.
  • useSelctor - 바뀐 값들을 스토어에 다시 가져옴.(state 역할)

4. index.js Provider store={store} 감싸기

profile
정말 최선을 다하겠습니다.

0개의 댓글

관련 채용 정보