항해하다/항해 - 4주차

[항해99 4주차, 장문주의] - Redux 개인과제 (3) [코드 뜯어보기]

위르겐 2022. 8. 3. 18:01

 

 

아무래도 코드설명을 하면

글이 길어지다보니

간단한 투두리스트 프로젝트도

블로그 포스팅이 나눠질 수 밖에 없는 듯.....

 

 

 

이제 남은 컴포넌트인

TodoItem과 Detail 그리고 모듈폴더의 todos.js 스토어를 살펴보자

 

 

 

 

<TodoItem.js>

const TodoItem = ({todo, i}) => {


  const dispatch = useDispatch();
  const onDelete = (id) => dispatch(deleteTodo(id))
  const onToggle = (id) => dispatch(toggleTodo(id))
  const navigate = useNavigate();
  const isDone = todo.isDone;


  return  (


    <>
    <StItem isDone={isDone}>
      <StP onClick={() => {navigate("/detail/" + todo.id)}}>
        상세보기
      </StP>
    <span>{todo.title}</span>
    <span>{todo.content}</span>
    <StDiv>
    <StButton onClick={() => { onDelete(todo.id) }}>삭제하기</StButton>
    <StButton onClick={() => { onToggle(todo.id)}}>
    {isDone ? "취소!" : "완료!"}
    </StButton>
    </StDiv>
    </StItem>
    </>
  )
}

 

 

제일 처음 App.js에서 봤던

detail라우팅이 여기에서 사용된다.

 

상세보기버튼을 

클릭할 시 navigate('/detail/' + tood.id)

경로로 이동하게 되는데

이 컴포넌트에서 사용되는 모든 todo는 

그 전의 TodoList에서 map함수를 사용하여 얻은 인자를 뜻한다.

 

즉 이 Item컴포넌트는 배열안의 객체 하나하나를 뜻하는 것이고

props로 받아온 todo는 

해당 객체를 뜻한다!

 

 

todo.id가 1인 투두리스트의

상세보기버튼을 누르게 되면

navigate로

http://localhost:3000/detail/1

위와 같은 경로이동이 되는 것이다!

 

새로고침없이 페이지가

부드럽게 이동되게 하는

리액트의 강력한 무기 중 하나라고 보면된다.

 

 

 

 

 

 

위 투두리스트 메인페이지에서

<TodoItem>컴포넌트는

 

 

이 리스트 하나를 뜻하는 것이다!

 

 

 

 

삭제하기와 완료를 누르면

각각 todos.js 스토어에 있는

toggleTodo와 deleteTodo가 실행되는데

id값을 파라미터로 넘겨 실행해준다.

 

 

 

 

<Detail.js>

 

const Detail = () => {
  const { id } = useParams();
  const todo_state = useSelector((state) => state.todo.todo_1); // 추가해주세요.
  const navigate = useNavigate();
  let todos = todo_state.find(data => data.id === Number(id));
  const isDone = todos.isDone;

  
  return (
    <>
      <GlobalStyle/>
      <StWrapper>
        <StItem isDone={isDone}>
          <h3>id : {todos.id}</h3>
          <Stbutton onClick={() => {navigate("/")}} style={{cursor:"pointer"}}>메인으로</Stbutton>
          <h1>{todos.title}</h1>
          <h3>{todos.content}</h3>
        </StItem>
      </StWrapper>
    </>
  );
};

 

 

리덕스 스토어에서 

마찬가지로 todo객체들이 있는 배열을 state로 가져오고 

그 배열을 find함수를 이용해 

해당 객체의 id와 URL에 입력된 숫자 {id}가 같다면

Detail메인화면을 띄워주는데 

useParams를 이용해 가져온 id값이 

문자열로 받아와져서 Number나 parseInt를 이용해 숫자로 형변환시켜줘야한다.

 

 

해당 객체의

id와 title그리고 content를 가져와 렌더링해주면

 

상세보기를 클릭할 때

 

http://localhost:3000/detail/1

위와 같은 URL로 이동되며

화면엔 

이러한 네모박스가 뜬다. ( 다음부턴 디자인신경좀...)

 

 

 

 

 

 

 

 

이제 대망의 하이라이트

리덕스 스토어 파일을 보자....

 

차근차근 하나하나 뜯어먹어보자!

 

 

<todos.js>

const ADD_TODO = "todos/ADD_TODO";
const DELETE_TODO = "todos/DELETE_TODO";
const TOGGLE_TODO = "todos/TOGGLE_TODO";

 

액션을 변수로 선언해 준 후 

이 액션을 

액션 creator에 type으로 지정해 줄 것이다.

 

 

 

 

let nextId = 1; // todo 데이터에서 사용 할 고유 id

export const addTodo = (title, content) => ({
  type: ADD_TODO,
  todo: {
    id: nextId++, // 새 항목을 추가하고 nextId 값에 1을 더해줍니다.
    title,
    content,
    isDone: false,
  },
});

export const deleteTodo = (id) => ({
  type: DELETE_TODO,
  id,
});

export const toggleTodo = (id) => ({
  type: TOGGLE_TODO,
  id,
});

 

 

addTodo와 deleteTodo, toggleTodo가 필요한 

각기다른 컴포넌트에서 

이 함수를 호출하여 사용하고 있는데

 

<TodoForm.js>컴포넌트에서 추가하기버튼을 누를 시 

dispatch(addTodo(title, content)) 함수가 실행되는걸 확인했었는데

title과 content 파라미터를 받아와

todo: { } 객체안에 id값과 isDone과 함께 넣어준다!

 

 

위의 export const로 되어있는 세 함수들을

Action Creator라고 한다..!

 

 

const initialState = {
  todo_1: [],
};

 

 

initialState는 초기상태값이라고 표현하는데

객체로 지정해줘도 되고

배열로해줘도되고

원시데이터로 지정해줘도 된다.

 

 

 

그렇지만 객체를 사용하면

각각 다른 데이터타입을가진

다양한 스테이트를 사용할 수 있다.

 

필자는 지금 

todo_1의 이름을가진 빈배열하나만 

초기상태값의 객체안으로 넣어주었다.

 

 

 

 

 

const todos = (state = initialState, action) => {
  switch (action.type) {
    case ADD_TODO:
      const addState = { ...state, todo_1: [...state.todo_1, action.todo] };
      return addState;

    case DELETE_TODO:
      const deleteFilter = { ...state, todo_1: state.todo_1.filter(todo => todo.id !== action.id)}
      return deleteFilter;

    case TOGGLE_TODO:
      const toggleState = { ...state, todo_1: state.todo_1.map(todo => todo.id === action.id ? { ...todo, isDone: !todo.isDone} : todo)}
      return toggleState;

    default:
      return state;
  }
};

 

 

자 이제 심호흡하고

 

후웁....

 

 

 

 

위의 함수를 Reducer라고 한다.

실제로 state가 이 리듀서에서 변경된다

 

switch case 조건문을 여기서 이용하게 될줄이야...

 

 

파라미터로는 state와 action을 받아오는데

state에 initialState를 할당한다.

 

리듀서 내에서 state를 불변성을 유지하며 변경해줄 수 있다.

 

 

 

switch case문을 통해

action.type이 ADD_TODO, DELETE_TODO, TOGGLE_TODO

일 때 각각 정해진 로직이 실행되는 것이다.

 

 

    case ADD_TODO:
      const addState = { ...state, todo_1: [...state.todo_1, action.todo] };
      return addState;

 

 

두번째 줄 로직은

현재 파라미터로 받아온 state가

 

const initialState = {
  todo_1: [],
};

 

위와 같기 때문에

todo_1이 아닌 다른 state들은 불변성을 ...state로 유지한 채 

todo_1 안의 값만 변경해준다!

 

그런데 액션크리에이터에서 

객체를 생성해줬으니까

todo_1 배열안에서

 그 생성된 객체 외에 다른 객체들은 

마찬가지로 ...state.todo_1 이렇게 불변성을 유지해준채

새로운 객체만 추가해주는 것이다.!

 

 

    case DELETE_TODO:
      const deleteFilter = { ...state, todo_1: state.todo_1.filter(todo => todo.id !== action.id)}
      return deleteFilter;

 

 

두번째 액션타입이 DELETE_TODO일 때 실행되는 로직은

마찬가지로 ...state로 불변성을 유지해준다.

 

state.todo_1의 배열을 filter함수로 돌린다.

 

클릭한 객체의 id 이외의 다른 id값을 가진 객체들을

반환해주면 클릭한 객체만 지우는 효과가 되는 것!

 

 

 

 

    case TOGGLE_TODO:
      const toggleState = { ...state, todo_1: state.todo_1.map(todo => todo.id === action.id ? { ...todo, isDone: !todo.isDone} : todo)}
      return toggleState;

 

 

클릭한 객체의 isDone값을 false와 true로 토글해주는 로직이다

 

map함수를 이용해 클릭한 객체의 id와 같은 값을 가진 객체의 isDone만

현재상태의 반대로 뒤집어 주는 것이다!

 

 

이렇게 했을 때

완료!버튼과 취소버튼을 누를 때마다

원하는 곳에 배치할 수 있게 된다.

 

 

 

 

 

 

 

 

이렇게 한번 쭉 훑어봤는데

 

이제 결과물을 봐보자....

 

 

 

 

 

 

 

다음 프로젝트는 CSS좀 이쁘게 꾸며서

보기 좋게 만들어봐야겠다...

 

지금보다 더..

 

 

 

 

react-redux는 이쯤하고

현재 투두리스트에 리덕스 툴킷을 덮어쓰기 하여 

돌아오겠읍니다...

 

반응형