import './App.css';
import { useState, useEffect } from "react";
function App() {
const [toDo, setToDo] = useState("");
const [toDos, setToDos] = useState([]);
const onChange = (event) => setToDo(event.target.value);
const onSubmit = (event) => {
event.preventDefault();
if (toDo === "") {
return;
}
if (toDos.length < 10) {
setToDos((currentArray) => [toDo, ...currentArray]);
} else {
return alert("등록된 리스트가 너무 많습니다.");
}
setToDo("");
};
const onClick = (idx) => {
setToDos(toDos.filter((_, toDoIdx) => idx !== toDoIdx));
}
useEffect(() => {
const data = localStorage.getItem('toDoList');
if (data) {
setToDos(JSON.parse(data));
}
}, []);
useEffect(() => {
localStorage.setItem('toDoList', JSON.stringify(toDos));
}, [toDos]);
return (
<div className="box">
<div className="todo">
<h1>너의 할 일은。</h1>
<span className="sub">"아직 한 적 없는 일을, 찾고 있어"</span>
<form
className="list"
onSubmit={onSubmit}>
<input
onChange={onChange}
maxLength={15}
value={toDo}
type="text"
placeholder="Write your to do..."
/>
<button>
<span className="material-symbols-outlined">add</span>
</button>
</form>
<ul>
{toDos.map((item, idx) => (
<li
key={idx}
>
{item}
<button onClick={() => onClick(idx)}>
<span className="material-symbols-outlined">delete_forever</span>
</button>
</li>
))}
</ul>
</div>
</div>
);
}
export default App;
input창에서 onChange
를 사용해 입력된 값을 toDo
와 setToDo
를 이용해 받는다.
form 태그에 쌓여진 button 태그가 하나일때, button 태그를 활성화한다면 form 태그 전체를 활성화시킬 수 있다. 이것을 이용해 button를 누르면, onSubmit
이 실행되도록 하자. onSubmit
으로 인해 제출된 toDo
는 값이 비었을 경우 아무것도 실행되지 않고, 값이 있을 경우 아래와 같은 코드로 실행된다.
setToDos((currentArray) => [toDo, ...currentArray]);
...
은 기존의 배열 값을 의미한다. 아래와 같은 예시를 들어보자
const str1 = [1, 2, 3, 4];
const str2 = [5, str1];
const str3 = [5, ...str1];
console.log(str2); // => [5, Array(4)]
console.log(str3); // => [5, 1, 2, 3, 4]
위의 예시대로 ...
을 사용하면 기존의 배열에 새로운 값을 더할 수 있다. 즉 기존의 toDo
에 새로운 값을 계속 더할 수 있다는 의미다.
toDo
가 onSubmit
됐다면, input 창을 비워주기 위해 setToDo("");
를 실행해준다. onSubmit
으로 등록된 toDo
값은 하단의 ul태그에서 map()
을 통해 출력된다.
map()
은 배열 내의 모든 요소 각각에 대하여 주어진 함수를 호출한 결과를 모아 새로운 배열을 반환한다. 사용방법은 아래와 같다.
arr.map(callback(currentValue, index, array), thisArg)
currentValue
: 현재 처리할 요소.index
: 처리할 현재 요소의 인덱스.array
: map()
을 호출한 배열.thisArg
: callback
을 실행할 때 this
로 사용되는 값.여기서는 item 값만 줘도 되나, index 값을 따로 주지 않으면 콘솔 창에서 key 값을 줘야한다고 경고 메세지가 뜬다.
여기까지 코드를 작성했으면, 아래와 같이 input 창에 To Do List를 작성하고 To Do List를 ul 태그에 출력할 수 있을 것이다.
너무 많은 글자를 작성하지 못 하도록 input 태그에 maxLength
를 사용하여 아래와 같이 글자 수를 제한할 수 있다.
<input maxLength={15} />
To DO List가 추가된 ul 태그의 button 태그에 onClick
기능을 추가해 filter()
를 활용한 함수를 실행하게 하자.
filter()
는 배열의 요소를 순차적으로 순회하면서 조건에 일치하는 요소를 모아 새로운 배열을 반환한다. 사용 방법은 아래와 같다.
arr.filter(callback(element, index, array), thisArg)
element
: 처리할 현재 요소.
index
: 처리할 현재 요소의 인덱스.
array
: filter
를 호출한 배열.
thisArg
: callback
을 실행할 때 this
로 사용하는 값.
filter()
를 테스트를 통과한 요소들로 이루어진 새로운 배열이 반환되며, 어떠한 요소도 통과하지 못하면 빈 배열이 반환된다. 예를들어, 아래와 같은 배열에서 5의 배수인 요소만 추출하고 싶을때 filter()
메서드를 사용하면 된다.
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const answer = arr.filter(arr => arr > 5)
console.log(answer); // => [6, 7, 8, 9]
onClick(idx)
을 통해 버튼을 클릭했을 때 삭제 기능을 넣은 함수를 실행하게 한 뒤 filter()
메서드를 사용해 코드를 아래와 같이 작성해보자. idx
는 toDos
의 배열 번호라고 생각하면 된다.
const onClick = (idx) => {
setToDos(toDos.filter((_, toDoIdx) => idx !== toDoIdx));
}
toDos
배열 안에서 filter()
메서드를 사용해서 배열 번호를 toDoidx
로 선언해주자. 앞의 element요소는 삭제 기능에 필요없는 부분이므로 언더스코어(_)를 써준다. 작성을 하지않으면 삭제 기능이 이루어지지 않으니 꼭 작성해주자. 그리고 익명함수를 통해 우리가 클릭한 배열 번호(삭제할 To Do)가 현재 toDos
가 가지고 있는 배열 번호 중 일치하지 않는 것만 출력해주도록 작성해주면 된다.
이렇게 코드를 작성하면 li에 속한 To Do List가 아래와 같이 삭제된다.
위의 To Do List 배열을 더해주는 코드에 if문을 씌워 10개 이하일때 동작하게하고, 10개 초과일시 alert()
를 실행해 알림 메세지를 띄워주자.
if (toDos.length < 10) {
setToDos((currentArray) => [toDo, ...currentArray]);
} else {
return alert("등록된 리스트가 너무 많습니다.");
}
To Do List를 LocalStorage에 저장하기 위해서는 useEffect()
를 사용해야 한다. useEffect()
는 React 컴포넌트가 렌더링될 때마다 특정 작업을 실행할 수 있도록하는 역할을 한다. useEffect()
는 컴포넌트가 처음 렌더링될때 한 번 실행되고, 배열로 지정된 useState()
의 값이 변경되면 다시 실행된다. 이러한 기능으로 하나의 값이 변동될때마다 전체 컴포넌트를 렌더링하지 않아 부담을 줄일 수 있다. 사용 방법은 아래와 같다.
const [name, setName] = useState();
useEffect(() => {
console.log("Hello!");
}, [name]);
첫 번째 렌더링이 될 때 console.log("Hello!")
를 한 번, name
이 변할 때마다 다시 console.log("Hello!")
를 한 번 더 출력하는 코드다. 위의 예시를 이용해 To Do List를 LocalStorage에 저장해보자.
useEffect(() => {
localStorage.setItem('toDoList', JSON.stringify(toDos));
}, [toDos]);
위와 같이 'toDoList'라는 이름으로 새로 저장되는 배열 toDos
를 LocalStorage에 저장할 수 있다.
이때 LocalStorage는 object를 저장할 수 없기에 JSON.stringify()
를 이용해 string으로 바꿔 저장할 수 있다.
useEffect(() => {
const data = localStorage.getItem('toDoList');
if (data) {
setToDos(JSON.parse(data));
}, []);
저장된 값은 위의 getItem()
을 이용해 불러올 수 있는데, 이때 string으로 변환된 값을 JSON.parse()
을 이용해 object 값으로 다시 바꿔줘야한다. 또한 LocalStorage가 비어있을 경우 아무 것도 출력되지 않도록 if문을 추가한다.
끝.
그림 배치도 잘하셨네요 👍👍 CSS도 천재되는중