일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |
- algoritms
- 알고리즘
- 모던자바스크립트
- pyhton
- 백준
- 혁신금융서비스
- 자료구조
- Python
- 로보어드바이저
- 자문형
- BFS
- Ai
- JS
- 스택
- programmers
- 자바스크립트
- JavaScript
- BAEKJOON
- 일임형
- algorithms
- 프로그래머스
- 큐
- 파이썬
- SSAFY
- React #Web #프런트엔드
- frontend
- Algorithm
- dfs
- 신한투자증권
- RPA
- Today
- Total
Step by Step
React Study(9) - 리액트를 다루는 기술 10장 본문
#10장 일정관리 웹 애플리케이션 만들기
구성 순서 : 프로젝트 준비하기 -> UI 구성하기 -> 기능 구현하기
10.1 프로젝트 생성 및 필요 라이브러리 설치
<index.css>
body {
margin: 0;
padding: 0;
background: #e9ecef;
}
<App.js>
const App = () => {
return <div>Todo App을 만들자!</div>
}
export default App;
10.2 UI 구성하기
TodoTemplate : 화면을 가운데에 정렬시키며, 앱 타이틀을 보여준다.
TodoInsert : 새로운 항목을 입력하고 추가할 수 있는 컴포넌트, state를 통해 인풋 상태 관리
TodoListItem : 각 할 일 항목에 대한 정보를 보여주는 컴포넌트이다. todo 객체를 props로 받아와서 상태에 따라 다른 스타일의 UI를 보여줌
TodoList : todos 배열을 props로 받아온 후 이를 배열 내장 함수 map을 사용해서 여러개의 TodoListItem 컴포넌트로 변환한다.
10.2.1 TodoTemplate 만들기
<TodoTemplate.scss>
.TodoTemplate {
width: 512px; //width가 주어진 상태에서 좌우 중앙 정렬
margin-left: auto;
margin-right: auto;
margin-top: 6rem;
border-radius: 4px;
overflow: hidden;
.app-title{
background: #22b8cf;
color:white;
height:4rem;
font-size: 1.5rem;
display: flex;
align-items: center;
justify-content: center;
}
.content{
background: white;
}
}
10.2.2 TodoInsert 만들기
<TodoInsert.js>
import {MdAdd} from 'react-icons/md';
import './TodoInsert.scss';
const TodoInsert = () => {
return (
<form className="TodoInsert">
<input placeholder="할 일을 입력하세요."/>
<button type="submit">
<MdAdd/>
</button>
</form>
);
};
export default TodoInsert;
사용하고 싶은 기호의 이름을 MdAdd처럼 넣으면 사용 가능하다.
<TodoInsert.scss>
.TodoInsert{
display:flex;
background: #495057;
input{
//기본 스타일 초기화
background: none;
outline: none;
border: none;
padding: 0.5rem;
font-size: 1.125rem;
line-height: 1.5;
color: white;
&::placeholder{
color:#dee2e6;
}
//버튼을 제외한 영역을 모두 차지하기
flex: 1;
}
button{
background: none;
outline:none;
border:none;
background:#868e96;
color: white;
padding-left: 1rem;
padding-right:1rem;
font-size: 1.5rem;
display: flex;
align-items:center;
cursor: pointer;
transition: 0.1s background ease-in;
&:hover{
background:#adb5bd;
}
}
}
10.2.3 TodoListItem 과 TodoList 만들기
일정 관리 항목이 보일 TodoListItem과 TodoList를 만들 차례이다
<TodoListItem.js>
import {
MdCheckBoxOutlineBlank,
MdCheckBox,
MdRemoveCircleOutline,
} from 'react-icons/md';
import './TodoListItem.scss';
const TodoListItem = () => {
return (
<div className="TodoListItem">
<div className="checkbox">
<MdCheckBoxOutlineBlank/>
<div className="text">할 일</div>
</div>
<div className="remove">
<MdRemoveCircleOutline/>
</div>
</div>
);
};
export default TodoListItem;
<TodoListItem.scss>
.TodoListItem{
padding: 1rem;
display: flex;
align-items:center;
&:nth-child(even){
background: #f8f9fa;
}
.checkbox{
cursor:pointer;
flex: 1;
display: flex;
align-items: center; // 세로 중앙 정렬
svg{
//아이콘
font-size: 1.5rem;
}
.text{
margin-left: 0.5rem;
flex : 1;//차지할 수 있는 모든 영역 차지
}
//체크되었을 때 보여줄 스타일
&.checked{
svg{
color:#22b8cf;
}
.text{
color:#adb5bd;
text-decoration: line-through;
}
}
}
.remove{
display: flex;
align-items: center;
font-size: 1.5rem;
color: #ff6b6b;
cursor: pointer;
&:hover{
color: #ff8787;
}
}
& + & {
border-top: 1px solid #dee2e6;
}
}
<TodoList.js>
import TodoListItem from './TodoListItem';
import './TodoListItem.scss';
const TodoList = () => {
return (
<div className>
<TodoListItem/>
<TodoListItem/>
<TodoListItem/>
</div>
);
};
export default TodoList;
<TodoList.scss>
.TodoList{
min-height: 320px;
max-height: 513px;
overflow-y: auto;
}
10.3 기능 구현하기
10.3.1 App에서 todos 사용하기
나중에 추가할 일정 항목에 대한 상태들은 모두 App 컴포넌트에서 관리한다.
App에서 useState를 사용하여 todos라는 상태를 정의하고 todos를 TodoList의 props로 전달한다.
<App.js>
import {useState} from 'react';
import TodoTemplate from './components/TodoTemplate';
import TodoInsert from './components/TodoInsert';
import TodoList from './components/TodoList';
const App = () => {
const [todos, setTodos] = useState([
{
id:1,
text: '리액트의 기초 알아보기',
checked: true,
},
{
id:2,
text: '컴포넌트 스타일링해보기',
checked: true,
},
{
id:3,
text: '일정 관리 앱 만들어보기',
checked:false,
},
]);
return (
<TodoTemplate>
<TodoInsert/>
<TodoList todos={todos}/>
</TodoTemplate>
)
}
export default App;
<TodoListItem.js>
import {
MdCheckBoxOutlineBlank,
MdCheckBox,
MdRemoveCircleOutline,
} from 'react-icons/md';
import cn from 'classnames';
import './TodoListItem.scss';
const TodoListItem = ({todo}) => {
const {text, checked} = todo;
return (
<div className="TodoListItem">
<div className={cn('checkbox', {checked})}>
{checked ? <MdCheckBox/> : <MdCheckBoxOutlineBlank/>}
<div className="text">{text}</div>
</div>
<div className="remove">
<MdRemoveCircleOutline/>
</div>
</div>
);
};
export default TodoListItem;
<TodoList.js>
import TodoListItem from './TodoListItem';
import './TodoListItem.scss';
const TodoList = ({todos}) => {
return (
<div className="TodoList">
{todos.map(todo=> (
<TodoListItem todo={todo} key={todo.id}/>
))}
</div>
);
};
export default TodoList;
10.3.2 항목 추가 기능 구현하기
TodoInsert 컴포넌트에서 인풋 상태를 관리하고 App 컴포넌트에는 todos 배열에 새로운 객체를 추가하는 함수를 만들어준다.
10.3.2.1 TodoInsert value 상태 관리하기
TodoInsert 컴포넌트에서 인풋에 입력하는 값을 관리할 수 있도록 useState를 사용하여 value라는 상태를 정의한다.
캄포넌트가 리렌더링될 때마다 함수를 새로 만드는 것이 아니라 한 번 함수를 만들고 재사용할 수 있도록 useCallback Hook을 사용해본다.
<TodoInsert.js>
import {useState, useCallback} from 'react';
import {MdAdd} from 'react-icons/md';
import './TodoInsert.scss';
const TodoInsert = () => {
const [value, setValue] = useState('');
const onChange = useCallback(e => {
setValue(e.target.value);
},[]);
return (
<form className="TodoInsert">
<input
placeholder="할 일을 입력하세요."
value={value}
onChange={onChange}/>
<button type="submit">
<MdAdd/>
</button>
</form>
);
};
export default TodoInsert;
10.3.2.3 todos 배열에 새 객체 추가하기
App 컴포넌트에서 todos 배열에 새 객체를 추가하는 onInsert 함수를 만든다.
새로운 객체를 만들 때마다 id값에 1씩 더해주어야 한다. id값은 useRef를 사용한다.
<App.js>
import {useState, useRef, useCallback} from 'react';
import TodoTemplate from './components/TodoTemplate';
import TodoInsert from './components/TodoInsert';
import TodoList from './components/TodoList';
const App = () => {
const [todos, setTodos] = useState([
{
id:1,
text: '리액트의 기초 알아보기',
checked: true,
},
{
id:2,
text: '컴포넌트 스타일링해보기',
checked: true,
},
{
id:3,
text: '일정 관리 앱 만들어보기',
checked:false,
},
]);
//고윳값으로 사용될 id
//ref를 사용하여 변수 담기
const nextId = useRef(4);
const onInsert = useCallback(
text => {
const todo = {
id: nextId.current,
text,
checked:false,
};
setTodos(todos.concat(todo));
nextId.current +=1; //nextId 1씩 더하기
},
[todos],
);
return (
<TodoTemplate>
<TodoInsert onInsert={onInsert}/>
<TodoList todos={todos}/>
</TodoTemplate>
)
}
export default App;
<TodoInsert.js>
import {useState, useCallback} from 'react';
import {MdAdd} from 'react-icons/md';
import './TodoInsert.scss';
const TodoInsert = ({onInsert}) => {
const [value, setValue] = useState('');
const onChange = useCallback(e => {
setValue(e.target.value);
},[]);
const onSubmit = useCallback(
e => {
onInsert(value);
setValue(''); // value 값 초기화
//submit 이벤트는 브라우저에서 새로고침을 발생시킴
e.preventDefault();
},
[onInsert,value],
);
return (
<form className="TodoInsert" onSubmit={onSubmit}>
<input
placeholder="할 일을 입력하세요."
value={value}
onChange={onChange}/>
<button type="submit">
<MdAdd/>
</button>
</form>
);
};
export default TodoInsert;
onSubmit이라는 함수를 만들고 이를 form의 onSubmit으로 지정했다.
이 함수가 호출되면 props로 받아온 onInsert함수에 현재 value 값을 파라미터로 넣어서 호출하고 현재 value값을 초기화한다.
onSubmit 대신에 onClick 이벤트로도 처리 가능하다.
const Onclick = useCallback(
() => {
onInsert(value);
setValue(''); //value 값 초기화
},
[onInsert, value],
);
10.3.3 지우기 기능 구현하기
배열의 불변성을 지키면서 배열 원소를 제거해야 할 경우, 배열 내장 함수인 filter를 사용하면 매우 편하다.
[filter 사용 예제]
const array = [1,2,3,4,5,6,7,8,9];
const biggerThanFive = array.filter(number => number>5);
//결과 : [6,7,8,9]
<App.js>
const onRemove = useCallback(
id => {
setTodos(todos.filter(todo => todo.id !==id));
},
[todos],
);
return (
<TodoTemplate>
<TodoInsert onInsert={onInsert}/>
<TodoList todos={todos} onRemove = {onRemove}/>
</TodoTemplate>
)
}
<TodoList.js>
import TodoListItem from './TodoListItem';
import './TodoListItem.scss';
const TodoList = ({todos, onRemove}) => {
return (
<div className="TodoList">
{todos.map(todo=> (
<TodoListItem todo={todo} key={todo.id} onRemove={onRemove}/>
))}
</div>
);
};
export default TodoList;
<TodoListItem.js>
import {
MdCheckBoxOutlineBlank,
MdCheckBox,
MdRemoveCircleOutline,
} from 'react-icons/md';
import cn from 'classnames';
import './TodoListItem.scss';
const TodoListItem = ({todo, onRemove}) => {
const {id, text, checked} = todo;
return (
<div className="TodoListItem">
<div className={cn('checkbox', {checked})}>
{checked ? <MdCheckBox/> : <MdCheckBoxOutlineBlank/>}
<div className="text">{text}</div>
</div>
<div className="remove" onClick={() => onRemove(id)}>
<MdRemoveCircleOutline/>
</div>
</div>
);
};
export default TodoListItem;
10.3.4 수정 기능
onToggle이라는 함수를 App에 만들고 해당 함수를 TodoList 컴포넌트에 props로 넣어준다.
<App.js>
const onToggle = useCallback(
id => {
setTodos(
todos.map(todo =>
todo.id === id ? {...todo, checked: !todo.checked} : todo,
),
);
},
[todos],
)
map을 사용하여 특정 id를 가지고 있는 객체의 checked 값을 반전시켰다.
불변성을 유지하면서 특정 배열의 원소를 업데이트할때는 map을 사용한다.
map 함수는 배열을 전체적으로 새로운 형태로 변환하여 새로운 배열을 생성해야할 때 사용한다고 배웠다.
onToggle 함수를 보면 todo.id === id ? : 이라는 삼항 연산자가 사용되었다.
todo.id와 현재 파라미터로 사용된 id값이 같을 때 새로운 객체를 생성하지만 다를때는 변화를 주지않고 처음 상태 그대로 반환한다.
10.4 정리
....
'React' 카테고리의 다른 글
React - Modal 실습 (0) | 2023.02.10 |
---|---|
React Study(8) - 리액트를 다루는 기술 9장 (0) | 2023.02.06 |
React Study(7) - 리액트를 다루는 기술 8장 (2) | 2023.02.02 |
React Study(6) - 리액트를 다루는 기술 7장 (3) | 2023.01.31 |
React Study(5) - 리액트를 다루는 기술 6장 (0) | 2023.01.31 |