// 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;
// 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을 추가하는 버튼
// 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 실행
+
버튼을 클릭했을 때, 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
// 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를 전달
// 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;
// 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()을 사용하려면 중괄호({ }) 안에 모든 표현식을 포함시켜야 한다.
// 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 추가