React

[React] TODOLIST

sejin2 2024. 3. 22. 10:37

구성하기 App.js

function App() {
  return (
    <div className="App"> 
      <Header />
      <Editor />
      <List />
    </div>
  );
}

export default App;

Header, Editor, List 컴포넌트로 만들기
Header.js

function Header() {
  return (
      <div>
          <h3>TODOLIST 🗓️</h3>
          <h2>{new Date().toDateString()}</h2>
      </div>
  )
}
export default Header;

Editor.js

const Editor = () => { 
    return (
        <div>
            <input placeholder="할일 추가" />
            <button>추가</button>
        </div>
    )
}
export default Editor

List.js

function List() {
	return (
	    <div className="List">
	        <h4>Todo List😀</h4>
	        <input placeholder="검색어를 입력하세요" />
	        <div className='todos_wrapper'>
            <TodoItem />
            <TodoItem />
            <TodoItem />
          </div>
	    </div>
	    )
	}
export default List


List.js에 들어갈 컴포넌트 TodoItem.js 생성

const TodoItem = () => {
    return (
        <div>
            <input type="checkbox" />
            <div>Todo...</div>
            <div>Date</div>
            <button>삭제</button>
        </div>
    )
}
export default TodoItem;

App.js

import './App.css';
import Header from './component/Header';
import Editor from './component/Editor'; 
import List from './component/List';
import { useState } from 'react';

const tmpData = [
  {
    id:0,
    isDone: false,
    content: "React 공부하기",
    data: new Date().getTime(),
  },
  {
    id:1,
    isDone: false,
    content: "꿀맛같은 휴식",
    data: new Date().getTime(),
  },
  {
    id:2,
    isDone: false,
    content: "친구화의 수다",
    data: new Date().getTime(),
  },
]

function App() {
  const [todos, setTodos] = useState(tmpData)
 

  const onCreate = (content) => {
    const newItem = {
      id :0,
      isDone :false,
      content: content,
      date : new Date().getTime()
    }
    setTodos([newItem, ...todos])
    ==> { }, { }, { } 이렇게 하나씩 풀어서 그 앞에 새로운 값을 넣어주고 다시 하나의 배열로 
        만든다. [ { }, { }, { }, { } ]
  }

  return (
    <div className="App"> 
      <Header />
      <Editor onCreate={onCreate} />
      <List />
    </div>
  );
}

export default App;

Editor.js

const Editor = ({onCreate}) => { 
    const [content, setContent] = useState("");

    return (
        <div className="Editor">
            <input onChange={(e) => {setContent(e.target.value)}} placeholder="할일 추가" />
            <button onClick={()=> {onCreate(content)}}>추가</button>
        </div>
    )
}
export default Editor

 

현재 추가한 항목들의 id가 다 0으로 나오는 것 해결해야 한다. ⇒ useRef ( )사용

App.js

import { useState, useRef } from 'react';

function App() {
  const [todos, setTodos] = useState(tmpData)
  const idRef = useRef(3)

  const onCreate = (content) => {
    const newItem = {
      id : idRef.current++,
      isDone :false,
      content: content,
      date : new Date().getTime()
    }
    setTodos([newItem, ...todos])
  }

  return (
    <div className="App"> 
      <Header />
      <Editor onCreate={onCreate} />
      <List />
    </div>
  );
}

데이터를 추가하면,

내용을 작성하지 않고, 추가를 누르면 빈 내용이 추가되는 것을 방지하기 위해 Editor.js에 코드를 추가해준다.

Editor.js

const Editor = ({onCreate}) => { 
    const [content, setContent] = useState("");
    const contentRef = useRef();

    return (
        <div className="Editor">
            <input onChange={(e) => {setContent(e.target.value)}} placeholder="할일 추가" />
            <button onClick={()=> {
                if(content == "") {
                    return;
                }
                onCreate(content)}
            }>추가</button>
        </div>
    )
}
export default Editor

+ 추가로 input 박스에 focus를 맞춰준다.

 return (
    <div className="Editor">
        <input ref={contentRef} onChange={(e) => {setContent(e.target.value)}} placeholder="할일 추가" />
        <button onClick={()=> {
            if(content == "") {
                contentRef.current.focus();
                return;
            }
            onCreate(content)}
        }>추가</button>
    </div>
)

++ 추가를 누르면 input 박스에 해당 글이 남지 않도록 지워준다.

 return (
    <div className="Editor">
        <input value={content} ref={contentRef} onChange={(e) => {setContent(e.target.value)}} placeholder="할일 추가" />
        <button onClick={()=> {
            if(content == "") {
                contentRef.current.focus();

                return;
            }
            onCreate(content)
            setContent("")
        }}> 추가</button>
    </div>
)

App.js에 있는 데이터들을 List목록에 띄워주어야 한다.

const tmpData = [
  {
    id:0,
    isDone: false,
    content: "React 공부하기",
    data: new Date().getTime(),
  },
  {
    id:1,
    isDone: false,
    content: "꿀맛같은 휴식",
    data: new Date().getTime(),
  },
  {
    id:2,
    isDone: false,
    content: "친구화의 수다",
    data: new Date().getTime(),
  },
]
 const [todos, setTodos] = useState(tmpData)

현재 todos에 담겨있음

<List todos={todos}/>

props로 넘겨준다.

List.js

function List({todos}) { 

    return (
        <div className="List">
            <h4>Todo List😀</h4>
            <input placeholder="검색어를 입력하세요" />
            <div className='todos_wrapper'>
                {
                    todos.map( todo => 
                        <TodoItem {...todo} />
                    )
                }
            </div>
        </div>
        )
}
export default List

TodoItem.js

const TodoItem = ({id, isDone, content, date}) => {
    return (
        <div className='TodoItem'>
            <input type="checkbox" checked={isDone} />
            <div className='content'>{content}</div>
            <div className='date'>{new Date(date).toLocaleDateString()}</div>
            <button>삭제</button>
        </div>
    )
}
export default TodoItem;

검색 기능 추가

List.js

function List({todos}) {
    const [search, setSearch] = useState("")
    
    const getFilterdData = () => {
        if(search == "") {
            return todos;
        }
        return todos.filter((todo) => 
            todo.content.includes(search) // includs() 함수 : todo.content와 search가 같으면 true반환, 다르면 false반환
        )
    }

    const filteredTodos = getFilterdData();

    return (
        <div className="List">
            <h4>Todo List😀</h4>
            <input placeholder="검색어를 입력하세요" onChange={(e)=>{setSearch(e.target.value)}}/>
            <div className='todos_wrapper'>
                {
                    filteredTodos.map(todo => 
                        <TodoItem {...todo} />
                    )
                }
            </div>
        </div>
        )
}
export default List

대소문자 구분 없이 검색하도록

⇒ todo.content와 search의 값을 비교하기 전에 전부 대문자 처리 OR 소문자 처리하도록 해준다.

 return todos.filter((todo) => 
      todo.content.toLowerCase().includes(search.toLowerCase()) 
      // includs() 함수 : todo.content와 search가 같으면 true반환, 다르면 false반환
  )

check 상태 바꾸기

App.js

const onUpdate = targetId => {
    setTodos(todos.map(todo=>{
      if(todo.id == targetId) {
        return {
          ...todo, 
          isDone: !todo.isDone  // true면 false로, false면 true로
        }
      }
      return todo
    }))
  }
  
위 코드를 삼항연산자로 줄이기

 const onUpdate = targetId => { 
    setTodos(todos.map(todo => todo.id == targetId? {...todo, isDone: !todo.isDone} : todo ))
  }


List로 넘겨주기

<List todos={todos} onUpdate={onUpdate} />

List.js

넘겨준 값 받기

function List({todos, onUpdate}) {

...

TodoItem으로 넘겨준다.

 {
    filteredTodos.map(todo => 
        <TodoItem {...todo} onUpdate={onUpdate} />
    )
}

TodoItem.js

넘겨준 값 받기

const TodoItem = ({id, isDone, content, date, onUpdate}) => {
    return (
        <div className='TodoItem'>
            <input type="checkbox" checked={isDone} onChange={()=>onUpdate(id)} />
            <div className='content'>{content}</div>
            <div className='date'>{new Date(date).toLocaleDateString()}</div>
            <button>삭제</button>
        </div>
    )
}
export default TodoItem;

체크가 잘되고,

isDone 값이 true로 잘 바뀐다.

삭제하기

App.js

const onDelete = targetId => {
    setTodos(todos.filter(todo => todo.id !== targetId));
  }
  
 <List todos={todos} onUpdate={onUpdate} onDelete={onDelete}/>

filter 함수를 사용하여 조건을 만족하지 않는 모든 요소를 유지하고, 삭제하려는 요소는 제거한다.
이 방법은 기존의 배열을 변경하지 않고 새로운 배열을 반환하여 요소를 삭제한다.

List.js

function List({todos, onUpdate, onDelete}) {

...

 <TodoItem {...todo} onUpdate={onUpdate} onDelete={onDelete} />

TodoItem.js

const TodoItem = ({id, isDone, content, date, onUpdate, onDelete}) => {
...
  <button onClick={()=>{onDelete(id)}}>삭제</button>

'React' 카테고리의 다른 글

[React] 리액트-스프링부트 연동하기  (0) 2024.03.22
[React] 로컬 스토리지  (0) 2024.03.22
[React] Context Api와 redux  (0) 2024.03.22
[React] axios  (0) 2024.03.22
[React] 리액트 부트스트랩 적용  (0) 2024.03.21