전체구조를 도식화 해보았다.
📌 구조 분해 할당
배열, 객체의 속성을 해체해서 그 값을 개별 변수에 담을 수 있게 하는 javascript 표현식
예시
let a, b;
[a, b]=[10, 20]
console.log(a) //10
console.log(b) //20
TodoListTemplate.js 에서 props를 인자로 받아야 하는 것을(props) → { ... ]
의 형태가 아닌
({form, children}) → {...}
의 형태로 작성
📌 e.stopPropagation() : 이벤트의 확산을 멈추는 기능
여기서는 하위요소의 x를 눌렀을 때 onRemove를 실행해주는데, onToggle에 영향이 가지않도록 하는 것
📌 최상위 컴포넌트 (부모컴포넌트)인 App에서 초기값 state 설정하기
📌 Form 기능 구현하기
import React, { Component } from "react";
import TodoListTemplate from "./components/TodoListTemplate";
import Form from "./components/Form";
import TodoItemList from "./components/TodoItemList";
class App extends Component {
id = 3;
state = {
input: "",
todos: [
{ id: 0, text: "리액트 소개", checked: false },
{ id: 1, text: "리액트 소개", checked: true },
{ id: 2, text: "리액트 소개", checked: false },
],
};
handleChange = (e) => {
this.setState({ input: e.target.value });
};
handleCreate = () => {
const { input, todos } = this.state;
this.setState({
input: "", //입력창을 비우는 작업이 반드시 필요
todos: todos.concat({
id: this.id++,
text: input,
checked: false,
}),
});
};
handleKeyPress = (e) => {
if (e.key === "Enter") {
this.handleCreate();
}
};
render() {
const { input } = this.state;
const { handleChange, handleCreate, handleKeyPress } = this;
return (
<div>
<TodoListTemplate
form={
<Form
value={input}
onKeyPress={handleKeyPress}
onChange={handleChange}
onCreate={handleCreate}
/>
}
>
<TodoItemList />
</TodoListTemplate>
</div>
);
}
}
export default App;
✅ state를 constructor에서 꺼내기
다른 방식으로 state의 초깃값을 지정해줄 수도 있는데, 여기서는 constructor를 이용한 방식이 아닌 바로 이 방식을 사용한다.
import React, { Component } from 'react';
class Counter extends Component {
state = {
number = 1
};
render(){
const { number } = this.state
return(...);
}
✅ this.setState 대신 함수 인자 전달하기
this.setState((prevState, props)=>{
return {
//업데이트할 내용
}
})
✅ state에서 배열을 다룰 때 push를 사용하면 안되는 이유
array.push() 메서드는 원본배열을 수정하지 않는다.
즉, 리랜더링시 배열을 비교해서 수정사항을 업데이트 해줘야 하는데, push의 경우 원본배열이 변하지 않으므로 변경된 사항이 제대로 반영이 되지 않는다.
따라서 state에서는 절.대❗❗❗ push를 사용하지 말자!
✅함수 비구조화 할당
const { handleChange, handleCreate, handleKeyPress } = this;
함수 호출시 this
를 계속 붙여야 하는 작업을 생략하는 방법이므로 유용하게 사용하자
값이 정상적으로 입력이 되고, 추가버튼을 클릭하거나 enter를 누르면 input 안에 내용은 '' , 즉 빈칸
이 되고, todos 배열에 객체형태로 추가되는 모습을 리액트 개발자 도구에서 확인할 수 있다.
📌 TodoItemList 에서 배열 -> TodoItem 배열로 변환 map 사용
import React, { Component } from "react";
import TodoItem from "./TodoItem";
class TodoItemList extends Component {
render() {
const { todos, onToggle, onRemove } = this.props;
const todoList = todos.map(({ id, text, checked }) => (
<TodoItem
id={id}
text={text}
checked={checked}
onToggle={onToggle}
onRemove={onRemove}
key={id}
/>
));
return <div>{todoList}</div>;
}
}
export default TodoItemList;
배열 랜더링 시 key값은 항상 존재해야 한다. key값이 있어야 컴포넌트가 재랜더링 될 때 효율적으로 작동한다.
handleToggle = (id) => {
const { todos } = this.state;
const index = todos.findIndex((todo) => todo.id === id);
const selected = todos[index];
const nextTodos = [...todos];
nextTodos[index] = {
...selected, //객체형태여서 스프레드 문법써서 풀어준것
checked: !selected.checked, //true면 false로, false면 true로
};
this.setState({
todos: nextTodos,
});
};
✅ 배열 복사
스프레드 문법(...)을 사용해서 배열을 풀어헤치고, 그걸 다시 새로운 변수를 지정해서 그 변수안에 배열의 형태로 담는다.(배열 복사)
스프레드 문법은 배열을 복사할 때 얕은 복사에서 효과적으로 동작한다. 다차원 배열을 복사하는 경우는 적합하지 않을 수 있다.
handleRemove = (id) => {
const { todos } = this.state;
this.setState({
todos: todos.filter((todo) => todo.id !== id),
});
};
filter를 사용해서 내가 선택한 바로 그 목록의 id가 없는 배열을 새로 반환한다. 따라서 이를 todos로 재할당하면 내가 선택한 바로 그 목록은 삭제가 된다.