connecting dots

Beginner 11회차 (8/13) | 화살표 함수와 코드 축약, store로 데이터 수정, 삭제 로직 옮기기, modal에 삭제 수정 기능 추가, promise(callback hell), useEffect(), 체크박스 기능 추가 본문

Live Class/Beginner

Beginner 11회차 (8/13) | 화살표 함수와 코드 축약, store로 데이터 수정, 삭제 로직 옮기기, modal에 삭제 수정 기능 추가, promise(callback hell), useEffect(), 체크박스 기능 추가

dearsuhyun 2024. 8. 14. 17:22

화살표 함수

화살표 함수 뒤 중괄호 잘 보고 return 키워드 잘 보기. 중괄호 없으면 그것이 어떤 데이터인지 살피기

 

코드 축약

너무 축약하는 것이 가독성이 낮다고 생각하면 그냥 if문 사용해도 됨. 축약형에 익숙해지기

 

Main.tsx 파일에 있던 setTodo(), deleteTodo() 함수 store로 이사 시키기

데이터를 불러 오는 getTodos()를 store로 이동시켜주었기 때문에

투두 리스트를 새로 수정하는 setTodo(), 투두를 삭제하는 deleteTodo() 역시 store로 이사를 시켜야 한다 !

데이터 권한이 이제 Main -> store로 넘어간 상황이니 수정, 삭제하는 부분도 store에서 제어해줘야 한다.

 

기존 Main.tsx

모두 store로 이동 !!!

 



store에서 가지고 올 수 있도록 TodoItem 파일 수정

 

옮긴 후 똑같이 작동하는 화면

 

모달에 삭제, 수정기능 추가

전체 코드

import styles from './TodoItemModal.module.css'
import { useNavigate, useParams } from 'react-router-dom'
import { useTodosStore } from '@/stores/todos'
import { useState } from 'react'

export default function TodoItemModal() {
  const navigate = useNavigate() // 훅 꺼내서 사용할게
  const { todoId } = useParams() // index.tsx에서 설정한 :todoId를 가져온다
  const todos = useTodosStore(state => state.todos) // 배열
  const deleteTodo = useTodosStore(state => state.deleteTodo)
  const updateTodo = useTodosStore(state => state.updateTodo)
  const currentTodo = todos.find(todo => todo.id === todoId) // 지금 id랑 주소창에 있는 id랑 같은 todo를 찾아서 가져온다
  const [title, setTitle] = useState(currentTodo?.title || '')
  const [done, setDone] = useState(currentTodo?.done || false)

  function offModal() {
    navigate('/') // 메인 페이지로 이동
  }

  function deleteCurrentTodo() {
    if (currentTodo) {
      deleteTodo(currentTodo)
      offModal()
    }
  }

  function updateCurrentTodo() {
    if (currentTodo) {
      updateTodo({
        ...currentTodo,
        title
      })
    }
  }

  return (
    <div className={styles.modal}>
      <div
        className={styles.overlay}
        onClick={offModal}></div>
      <div className={styles.contents}>
        <div>{currentTodo?.title}</div>
        <div>{currentTodo?.createdAt}</div>
        <input
          type="checkbox"
          checked={done}
          onChange={e => {
            setDone(e.target.checked)
            if (currentTodo) {
              updateTodo({
                ...currentTodo,
                done: e.target.checked
              })
            }
          }}
        />
        <input
          value={title}
          onChange={e => setTitle(e.target.value)}
        />

        <button onClick={updateCurrentTodo}>수정</button>
        <button onClick={deleteCurrentTodo}>삭제</button>
      </div>
    </div>
  )
}

 

 

삭제 코드 뜯어보기

store에서 데이터 다루기 위해 useTodosStore 가지고 오기

// store
async function deleteTodo(deletedTodo: Todo) {
        await fetch(
          `https://asia-northeast3-heropy-api.cloudfunctions.net/api/todos/${deletedTodo.id}`, //:todoId
          {
            method: 'DELETE',
            headers: {
              'content-type': 'application/json',
              apikey: 'KDT9_AHMq2s7n',
              username: 'FE1_ParkSuHyun'
            }
          }
        )
        getTodos()
      }
import { useTodosStore } from '@/stores/todos'

const todos = useTodosStore(state => state.todos) // 배열
const deleteTodo = useTodosStore(state => state.deleteTodo)
const currentTodo = todos.find(todo => todo.id === todoId) 
// 지금 id랑 주소창에 있는 id랑 같은 todo를 찾아서 가져온다

 

버튼을 누르면 deleteCurrentTodo 함수 실행 --> 현재 todo를 지우는 로직 실행, offModal 함수로 메인페이지로 이동

function offModal() {
    navigate('/') // 메인 페이지로 이동
  }

  function deleteCurrentTodo() {
    if (currentTodo) { // 현재 todo
      deleteTodo(currentTodo)
      offModal()
    }
  }
  
return (
  <button onClick={deleteCurrentTodo}>삭제</button>
)

 

수정 코드 뜯어보기

store에서 데이터 수정하기 위해 updateTodo가지고 오기

 // store
 async function updateTodo(updatedTodo: Todo) {
    try {
      await fetch(
        `https://asia-northeast3-heropy-api.cloudfunctions.net/api/todos/${updatedTodo.id}`,
        {
          method: 'PUT',
          headers: {
            'content-type': 'application/json',
            apikey: 'KDT9_AHMq2s7n',
            username: 'FE1_ParkSuHyun'
          },
          body: JSON.stringify({
            title: updatedTodo.title,
            done: updatedTodo.done
          })
        }
      )
      getTodos()
    } catch (error) {
      console.error(error)
    }
  }
import { useTodosStore } from '@/stores/todos'

const updateTodo = useTodosStore(state => state.updateTodo)
const currentTodo = todos.find(todo => todo.id === todoId)

 

수정 버튼 누르면 updateCurrentTodo 함수 실행 --> currentTodo를 업데이트 된 todo로 바꿔줌

function updateCurrentTodo() {
    if (currentTodo) {
      updateTodo({
        ...currentTodo,
        title
      })
    }
  }
  
return (
  <input
    value={title}
    onChange={e => setTitle(e.target.value)}
  />
  <button onClick={updateCurrentTodo}>수정</button>
)

 

현재 수정 시 offModal을 사용하지 않아서 랜딩 페이지로 넘어가지 않지만 밑 코드처럼 추가해주면

삭제버튼 눌렀을 때와 같이 내용 수정과 랜딩 페이지로 돌아가게 구현됨 !

function updateCurrentTodo() {
    if (currentTodo) {
      updateTodo({
        ...currentTodo,
        title
      })
      offModal()
    }
  }

--> 사실 useEffect()를 사용해서 todo가 변경될 때마다 title을 변경하도록 해주어야 위 구현 영상처럼 input 부분도 바뀐 todo의 title로 잘 반영될 수 있음 !

 

useEffect()

의존성 배열에 데이터를 넣으면, 데이터 변경 시 콜백함수가 실행됨 !

 

useEffect는 React에서 제공하는 훅(hook) 중 하나로, 함수형 컴포넌트가 렌더링될 때마다 특정 작업(사이드 이펙트)을 수행할 수 있게 해줍니다. 사이드 이펙트란 컴포넌트의 렌더링 외에 발생하는 모든 작업을 의미합니다. 예를 들어, 데이터를 가져오거나, 타이머를 설정하거나, 브라우저 API를 사용하는 작업 등이 사이드 이펙트입니다.

- 기본 구조
useEffect(() => {
  // 여기에 실행할 코드를 작성합니다.
}, [의존성 배열]);​

첫 번째 매개변수: 실행할 코드를 함수로 정의합니다. 이 함수가 컴포넌트가 렌더링될 때마다 실행됩니다.

두 번째 매개변수 (의존성 배열): 이 배열에 포함된 값들이 변경될 때만 useEffect 안의 함수가 실행됩니다. 배열이 비어 있으면, 컴포넌트가 처음 렌더링될 때 한 번만 실행됩니다.

- 예제

import React, { useState, useEffect } from 'react';

function ExampleComponent() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log(`count가 변경되었습니다: ${count}`);
  }, [count]);

  return (
    <div>
      <p>{count}</p>
      <button onClick={() => setCount(count + 1)}>증가</button>
    </div>
  );
}

 

체크박스 만들기

// todoItemModal

const [done, setDone] = useState(currentTodo?.done || false)

return (
  <input
  type="checkbox"
  checked={done}
  onChange={e => {
    setDone(e.target.checked)
    if (currentTodo) {
      updateTodo({
        ...currentTodo,
        done: e.target.checked
      })
    }
  }}
)

// todoItem
const [done, setDone] = useState(todo.done)

useEffect(() => {
    setTItle(todo.title)
    setDone(todo.done)
  }, [todo])

return (
<input
        type="checkbox"
        checked={done}
        onChange={e => {
          setDone(e.target.checked)
          updateTodo({
            ...todo,
            done: e.target.checked
          })
        }}
      />
)

 

콜백지옥(callback hell)

 

--> 해결 방법 promise !

await의 경우에는 promise 인스턴스를 반환하는 함수 앞에만 붙일 수 있음 !

원래 promise 인스턴스를 반환하는 함수가 아니더라도 앞에 async 붙여주면 await 사용 가능

(async 키워드가 붙은 함수는 언제나 promise 인스턴스를 반환한다 !)

promise 생성자 함수에서는 무조건 resolve 호출을 해줘야 함

 

 


 

map 메소드: 새로운 배열 데이터(콜백에서 모아서) 반환

리팩토링: 코드 기능은 달라지지 않았지만 코드 내용은 바뀌는 것 (원래의 기능, 동작은 그대로 but 코드만 수정)
만약 새로운 기능이 생긴다면 ? --> 기능 개발 !

 

 

https://www.heropy.dev/p/N6phSt

 

JS 함수 핵심 패턴

자바스크립트 함수에 대한 기본적인 사용 방법부터 관련 용어, 고급 기법 등의 관련된 다양한 함수 패턴과 여러 개념을 살펴봅니다.

www.heropy.dev

https://www.heropy.dev/p/OidQSe

 

React 핵심 패턴

React 기본 문법의 다양한 사용 패턴을 살펴봅니다.

www.heropy.dev

 

반응형