프로젝트로 정리하는 리액트 (1)

이동건 (불꽃냥펀치)·2025년 3월 8일

create/Update/Delete

import { useState,useReducer,useRef } from 'react'
import './App.css'
import { Routes,Route,Link ,useNavigate} from 'react-router-dom'
import Home from './page/Home'

const mockData=[
  {
    id:1,
    createdDate:new Date('2025-01-08'),
    emotionId:1,
    content:'일번 일기내용'
  },
  {
    id:2,
    createdDate:new Date('2025-01-18'),
    emotionId:2,
    content:'2번 일기내용'
  },
  {
    id:3,
    createdDate:new Date('2025-01-28'),
    emotionId:3,
    content:'3번 일기내용'
  },
]
function reducer(state,action){
  switch(action.type){
      case "CREATE" : return [...state,action.data] ;
      case "UPDATE" : return state.map((item)=>String(item.id)===String(action.data.if)? action.data: item);
      case "DELETE" : return state.filter((item)=> String(item.id)!==String(action.data.id));
      default : return state;
  }
}
function App() {
  const idRef=useRef(3);
  const [count, setCount] = useState(0)
  const [data,dispatch] = useReducer(reducer,mockData);
  const onCreate=(createdDate,emotionId,content)=>{
    dispatch({
      type:"CREATE",
      data:{
        id:idRef.current++,
        createdDate,
        emotionId,
        content
      }
    })
  }

  const onDelete=(id)=>{
    dispatch({
      type:"DELETE",
      data:{
        id
      }
    })
  }

  const onUpdate=(id,createdDate,content,emotionId)=>{
    dispatch({
      type:"UPDATE",
      data:{
        id,
        createdDate,
        emotionId,
        content
      }
    })
  }
  return (
    <>
      <Home></Home>
    </>
  )
}

export default App

경로 설정하기

 return (
    <>
      <DiaryStateContext.Provider value={data}>
        <DiaryDispatchContext.Provider value={{onCreate,onDelete,onUpdate}}>
          <Routes>
              <Route path='/' element={<Home></Home>}></Route>
              <Route path='/new' element={<New></New>}></Route>
              <Route path='/diary/:id' element={<Diary></Diary>}></Route>
              <Route path='*' element={<NotFound></NotFound>}></Route>
              <Route path='/edit/:id' element={<Edit></Edit>}></Route>
          </Routes>
        </DiaryDispatchContext.Provider>
      </DiaryStateContext.Provider>
    </>
  )
}
  • * : 지정되지 않은 루트로 이동하면 정상적이지 않은 페이지라고 알려주는 페이지로 이동시킴
  • createContextprops를 이용하여 부모에서 자식으로 전달이 아닌 어디에서나 접근이 가능하게 만듦
  • /edit:id : /edit/3과 같은 url로 접근하면 해당 아이디가 가지고 있는 정보를 나타내는 페이지로 이동

주의

 <BrowserRouter>
        <App />
    </BrowserRouter>
  • Routes를 쓸때는 AppBrowserRouter로 감싸져 있어야 함



'/' Home page 구축

HomePage 이미지

  • 이렇게 만들기 위해 components 폴더를 만든 후 Header,Button,DiaryList,DiaryItem 컴포넌트를 구현함

Header

const Header=({title,leftChild,rightChild})=>{
    return <header className='Header'>
            <div className='header_left'>{leftChild}</div>
            <div className='header_center'>{title}</div>
            <div className='header_right'>{rightChild}</div>
        </header>

}

export default Header
  • title에는 텍스트 구문 leftChild,rightChild의 경우는 Button 컴포넌트를 상속받음

Button

const Button=({text,type,onClick})=>{
    return   <button className={`Button Button_${type}`} onClick={onClick} > {text}</button>
}

export default Button
  • 버튼의 타입에 따라 다른 css를 적용하기 위해 동적으로 클래스 생성
  • 상속받은 onClick 함수 적용

DiaryList

const DiaryList=({data})=>{
    
    const nav=useNavigate();
    const [sortType,setSortType]=useState("latest");
    const onChangeSortType=(e)=>{
        setSortType(e.target.value);
    }
    // 원본배열을 수정하면 문제가 생길수있어 새로운 배열로 반환받음
    const getSortedData=()=>{
        return data.toSorted((a,b)=>{
            if(sortType=="oldest"){
                return Number(a.createdDate) -Number(b.createdDate);
            }else{
                return Number(b.createdDate)-Number(a.createdDate);
            }
        })
    }

    const sortedData =getSortedData();
    return <>
    
    <div className="DiaryList">
        <div className="menu_bar">
            <select name="" id="" onChange={onChangeSortType}>
                <option value="latest">최신순</option>
                <option value="oldest"> 오래된순</option>
                
            </select>
            <Button text={'새로운 일기 쓰기'} type={'POSITIVE'} onClick={()=>{
                nav("/new")
            }}></Button>
        </div>
        <div className="list_wrapper">
            {sortedData.map((item)=>{
                return <DiaryItem key={item.id}{...item}></DiaryItem>
            })}
        </div>
    </div>
    
    </>
}

export default DiaryList
  • 상속받은 데이터를 조작하면 문제가 될 수 있어서 함수로 새 데이터를 반환하게 함
  • 최신순 , 오래된순 을 각각 클릭하면 onChangeSortType에서 setSortType으로 타입 조작과 동시에 getSortedData()를 새로 반환
  • 그렇게 정렬한 데이터를 map을 써서 배열을 순환하며 각 데이터를 DiaryItem으로 props형태로 전달

DiaryItem

const DiaryItem=({id,emotionId,createdDate,content})=>{
    const nav=useNavigate();
    return (
        <div className="DiaryItem">
            <div className={`img_section img_section_${emotionId}`} onClick={()=>nav(`/diary/${id}`)}>
                <img src={getEmotionImage(emotionId)} alt="" />
            </div>
            <div className="info_section" onClick={()=>nav(`/diary/${id}`)}>
                <div className="created_date">{new Date(createdDate).toLocaleDateString()}</div>
                <div className="content">
                    {content}
                </div>
            </div>
            <div className="button_section">
                <Button text={'수정하기'} onClick={()=>nav(`/edit/${id}`)}></Button>
            </div>
        </div>
    )

}

export default DiaryItem
  • getEmtionImage(): 별도의 자바 스크립트 파일로 만들어 emotionId에 따라 다른 이미지 전달
  • 수정버튼이나 이미지를 클릭하면 수정페이지와 상세보기 페이지로 이동
profile
자바를 사랑합니다

0개의 댓글