[나작프] 003. Todo List(3)

김현주·2022년 5월 26일
0

나의작은프로젝트

목록 보기
7/9

1. 이름 구현하기

1-1) App.js

// App.js
...

function App(){
  return(
    <>
      <div className={AppStyle.container} style={{ backgroundColor: `${bgColor}` }}>
        <h1 className={AppStyle.title}>TODO LIST</h1>
        <Color setBgColor={setBgColor} />
      </div>
    </>
  )
}
export default App;

2. Input 구현

2-1) input에 입력한 값 가져오기

// Input.js
import InputStyle from '../Css/Input.module.css';
import { useState } from 'react';

function Input(){
  const [todo, setTodo] = useState(''); // (1)
  function onInputChange(e){ // (2)
    setTodo(e.target.value);
  };
  
  return(
    <>
      <form type='submit' className={InputStyle.container}>
        // (3)
        <input
          type='text' 
          className={InputStyle.input} 
          value={todo}
          placeholder='오늘의 할 일을 적어주세요'
          onChange={onInputChange} // (4)
        />
        // (5)
        <button 
          type='submit'
          className={InputStyle.addButton}
        >+</button>
      </form>
    </>
  )
}
export default Input;

(1) useState() Hook을 사용하여 빈 문자열인 todo와 setTodo()를 생성
(2) input이 변할 때 input의 값을 가져오는 함수
(3) item을 추가하는 input란
(4) input이 변할 때 onInputChange()를 실행
(5) input에 값을 입력 후 item을 추가하는 버튼

2-2) 추가버튼 클릭시 input 초기화 & focus

// Input.js
import InputStyle from '../Css/Input.module.css';
import { useState, useRef } from 'react';

function Input(){
  const [todo, setTodo] = useState('');
  const inputRef = useRef(null); // (1)
  function onInputChange(e){
    setTodo(e.target.value);
  };
  function addButtonClick(e){ // (2)
    e.preventDefault();
    setTodo(''); // (2)-1
    inputRef.current.focus(); // (2)-2
  }
  
  return(
    <>
      <form type='submit' className={InputStyle.form}>
        <input
          type='text'
          className={InputStyle.input} 
          value={todo}
          placeholder='오늘의 할 일을 적어주세요'
          onChange={onInputChange}
          ref={inputRef} // (3)
        />
        <button 
          type='submit'
          className={InputStyle.addButton}
          onClick={addButtonClick} // (4)
        >+</button>
      </form>
    </>
  )
}
export default Input;

(1) useRef() Hook을 사용하여 inputRef 객체 생성

⭐ useRef()?
const refContainer = useRef(초기값);
→ useRef는 특정 DOM을 선택할 때 사용한다.
→ refContainer 객체는 선택할 DOM에 ref값으로 설정해야한다.
→ Ref객체의 .current값은 원하는 DOM을 가르킨다.

(2) addButtonClick() 메서드 생성
(2)-1 빈 문자열로 만들어서 초기화
(2)-2 inputRef.current로 input에 focus된다.
(3) (1)에서 만든 inputRef를 input에 지정
(4) +버튼 클릭시 addButtonClick 실행

2-3) TodoList에 item 추가

  • +버튼을 클릭했을 때, TodoList에 todo값 넣기
// App.js
...
import { useState } from 'react';
import Input from './Components/Input';

function App(){
  const [todoList, setTodoList] = useState([]); // (1)
  return(
    <>
      ...
      <Input todoList={todoList} setTodoList={setTodoList} /> // (2)
      <TodoList title={'To Do'} /> // (3)
      ...
    </>
  )
}
export default App;

(1) todo item들을 담을 todoList와 setTodoList()를 useState를 사용하여 생성, 이 때 빈 배열을 초기값으로 갖는다.
(2) Input 컴포넌트에 props를 통해 todoList와 setTodoList를 전달

// Input.js
import InputStyle from '../Css/Input.module.css';
import { useState, useRef } from 'react';
import PropTypes from 'prop-types';

function Input({ todoList, setTodoList }){ // (1)
  const [todo, setTodo] = useState('');
  const inputRef = useRef(null);
  function onInputChange(e){
    setTodo(e.target.value);
  };
  function addButtonClick(e){
    e.preventDefault();
    const addTodoList = todoList.concat({ // (2)
      id: todoList.length, // (2)-1
      todo, // (2)-2
    });
    setTodoList(addTodoList);
    
    setTodo('');
    inputRef.current.focus();
  }
  
  return(
    <>
      <form type='submit' className={InputStyle.form}>
        <input
          type='text'
          className={InputStyle.input} 
          value={todo}
          placeholder='오늘의 할 일을 적어주세요'
          onChange={onInputChange}
          ref={inputRef}
        />
        <button 
          type='submit'
          className={InputStyle.addButton}
          onClick={addButtonClick}
        >+</button>
      </form>
    </>
  )
}
// (3)
Input.PropTypes = {
  todoList = PropTypes.arrayOf(
    PropTypes.shape({
      id: PropTypes.number.isRequired,
      todo: PropTypes.string.isRequired,
    }).isRequired
  ),
  setTodoList = PropTypes.func.isRequired,
}
export default Input;

(1) 부모 컴포넌트인 App으로 부터 props된 todoList와 setTodoList를 받아온다.
(2) input에 적은 todo를 setTodoList()를 사용하여 todoList에 저장한다.

⭐ concat()?
→ concat 함수는 기존의 배열을 수정하지 않고, 새로운 원소가 추가된 새로운 배열을 만들어줍니다.

(2)-1 todo의 각 item에 id를 지정한다. (todolist의 길이를 사용)
(2)-2 각 todo item의 내용
(3) prop-types를 사용하여 props의 타입을 지정한다.

⭐ prop-types?
→ PropTypes는 전달받은 데이터의 유효성을 검증하기 위한 다양한 유효성 검사기(Validator)
① npm add prop-types
② import PropTypes from 'prop-types';
③ 컴포넌트명.PropTypes = {
// prop가 특정 JS 형식임을 선언할 수 있습니다.
// 이것들은 기본적으로 모두 선택 사항입니다.
optionalArray: PropTypes.array,
optionalBool: PropTypes.bool,
optionalFunc: PropTypes.func,
optionalNumber: PropTypes.number,
optionalObject: PropTypes.object,
optionalString: PropTypes.string,
optionalSymbol: PropTypes.symbol,
// 특정 형태를 갖는 객체
optionalObjectWithShape: PropTypes.shape({
color: PropTypes.string,
fontSize: PropTypes.number
})
}
출처 : https://ko.reactjs.org/docs/typechecking-with-proptypes.html


3. TodoList 구현

3-1) TodoList에 todo item 출력

// App.js
...
import { useState } from 'react';
import TodoList from './Components/TodoList';

function App(){
  const [todoList, setTodoList] = useState([]);
  return(
    <>
      ...
      <TodoList todoList={todoList} setTodoList={setTodoList} /> // (1)
      ...
    </>
  )
}
export default App;

(1) TodoList 컴포넌트에 props를 사용하여 todoList와 setTodoList를 전달

3-2) TodoList에 넣을 TodoItem 생성

  • TodoItem 컴포넌트의 기본적인 구조를 생성
// TodoItem.js
import itemStyle from '../Css/TodoItem.module.css';

function TodoItem(){
  return(
    <>
      <li className={itemStyle.item}>
        <input type='checkbox' />
        <span>{/* todoitem의내용 */}</span>
        <button type='button' className={itemStyle.editButton}>🔺</button>
        <button type='button' className={itemStyle.deleteButton}></button>
      </li>
    </>
  )
}
export default TodoItem;

3-3) TodoList에 TodoItem 컴포넌트 넣기

// TodoList.js
import listStyle from '../Css/TodoList.module.css';
import TodoItem from './TodoItem';
import PropTypes from 'prop-types';

function TodoList({ todoList, setTodoList }){
  return(
    <>
      <div className={listStyle.container}>
        <ul className={listStyle.list}>
          todoList.map((todoItem) => { // (1)
            return <TodoItem // (2)
                     key={todoItem.id}
                     todoItem={todoItem}
                     todoList={todoList}
                     setTodoList={setTodoList}
                   />
          });
        </ul>
      </div>
    </>
  )
}
TodoList.PropTypes = {
  todoList: PropTypes.arrayOf(
    PropTypes.shape({
      id: PropTypes.number.isRequired,
      todo: PropTypes.string.isRequired
    })
  ),
  setTodoList: PropTypes.func.isRequired,
};

export default TodoList;

(1) 배열을 반복문으로 출력하기 위해서는 map()함수를 사용
(2) TodoItem 컴포넌트에 key, todoItem, todoList, setTodoList를 전달

⭐ map()?
array.map( callback함수 )
→ map() 메서드는 배열 내의 모든 요소 각각에 대하여 주어진 함수를 호출한 결과를 모아 새로운 배열을 반환한다.
→ key : React가 어떤 항목을 변경, 추가 또는 삭제할지 식별하는 것을 돕고, 요소에 안정적인 고유성을 부여하기 위해 배열 내부의 요소에 지정해야 한다.
→ JSX에서 map()을 사용하려면 중괄호({ }) 안에 모든 표현식을 포함시켜야 한다.

3-4) TodoItem에 내용 추가

// TodoItem.js
import itemStyle from '../Css/TodoItem.module.css';
import PropTypes from 'prop-types';

function TodoItem({ todoItem, todoList, setTodoList }){
  return(
    <>
      <li className={itemStyle.item}>
        <input type='checkbox' />
        <span>{ todoItem }</span> // (1)
        <button type='button' className={itemStyle.editButton}>🔺</button>
        <button type='button' className={itemStyle.deleteButton}></button>
      </li>
    </>
  )
}
TodoItem.PropTypes = {
  todoItem: PropTypes.shape({
    id: PropTypes.number.isRequired,
    todo: PropTyes.string.isRequired,
  }),
  todoList: PropTypes.arrayOf(
    PropTypes.shape({
      id: PropTypes.number.isRequired,
      todo: PropTyes.string.isRequired,
    })
  ),
  setTodoList: PropTypes.func.isRequired,
};
export default TodoItem;

(1) TodoList에서 가져온 todoItem 추가


4. 결과

profile
✨프론트엔드 개발자가 되기 위한 독학러✨

0개의 댓글