할 일 목록 부분을 위한 컴포넌트 생성하기 (컴포넌트 분리하기)
지금까지 하나의 컴포넌트에 모두를 넣었지만,
컴포넌트를 분리한다면 구분하기도 쉽고, 재사용성을 높일 수 있다.
component 폴더를 생성하고 안에 List.js 파일을 생성해준다.
그리고 함수형 컴포넌트를 작성해준다. (rfc + enter을 하면 바로 함수형 컴포넌트가 자동으로 작성된다.)
(여담으로 rce + enter를 하면 클래스 컴포넌트)
import React from 'react'
export default function List() {
return (
<div>List</div>
)
}
그리고, App.js 코드의 상단에 List를 import해오기 위해,
import List from "./components/List";
를 작성해준다.
App.js에서 리스트에 관련된 부분은, 리스트를 나열해주는 부분인
{todoData.map((data) => (
<div style={getStyle(data.completed)} key={data.id}>
<input type="checkbox"
defaultChecked={false}
onChange={() => handleCompleteChange(data.id)} />
{data.title}
<button style={btnStyle}
onClick={() => handleClick(data.id)}>x</button>
</div>
))}
이부분을 가져오고, 버튼과 관련된 부분인
const btnStyle = {
color: "#fff",
border: "none",
padding: "5px 9px",
borderRadius: "50%",
cursor: "pointer",
float: "right"
}
이부분도 List.js로 가져온다.
const [todoData, setTodoData] = useState([]);
이 부분을 List.js로 가져오는것 보단 todoData를 Props로 내려주는 것이 더 좋다.
그래서 할일 목록 아래에 다음과 같이 내려준다.
return(
<div className="container">
<div className="todoBlock">
<div className="title">
<h1>할 일 목록</h1>
</div>
<List todoData={todoData} setTodoData={setTodoData} /> //이렇게 말이다.
이렇게 내려주면 부모 컴포넌트에서 내려준 Props를 사용할 수 있다.
export default function List({todoData, setTodoData}) {
이렇게 List.js에서 말이다.
이제 필요한 함수인 getStyle, handleClick, handleCompleChange를 App.js에서 List.js로 가져온다.
위의 과정을 모두 수행하면 App.js와 List.js의 코드는 다음과 같다. 컴포넌트를 분리하기 전과 똑같이 작동한다.
<App.js>
import React, {useState} from "react"; //react는 라이브러리이므로 컴포넌트를 가져와서
import "./App.css"
import List from "./components/List";
export default function App() {
const [todoData, setTodoData] = useState([]);
const [value, setValue] = useState("");
const handleChange = (e) => {
setValue(e.target.value)
}
const handleSubmit = (e) => {
e.preventDefault(); //페이지가 리로드 되는 것을 막아줌.
//새로운 할 일 데이터
let newTodo = {
id: Date.now(),
title: value,
completed: false
}
//원래 있던 할 일에 새로운 할 일 더해주기
setTodoData(prev => [...prev, newTodo]);
setValue("");
}
return(
<div className="container">
<div className="todoBlock">
<div className="title">
<h1>할 일 목록</h1>
</div>
<List todoData={todoData} setTodoData={setTodoData} />
<form style={{ display: 'flex'}} onSubmit={handleSubmit}>
<input
type='text'
name="value"
style={{ flex: '10', padding: '5px'}}
placeholder="해야 할 일을 입력하세요."
value={value}
onChange={handleChange}
/>
<input
type="submit"
value="입력"
className="btn"
style={{flex: '1'}}
/>
</form>
</div>
</div>
)
}
<List.js>
import React from 'react'
export default function List({todoData, setTodoData}) {
const btnStyle = {
color: "#fff",
border: "none",
padding: "5px 9px",
borderRadius: "50%",
cursor: "pointer",
float: "right"
}
const getStyle = (completed) => {
return {
padding: "10px",
borderBottom: "1px #ccc dotted",
textDecoration: completed ?"line-through" :"none"
}
}
const handleClick = (id) => {
let newTodoData = todoData.filter(data => data.id !== id);
console.log('newTodoData', newTodoData);
setTodoData(newTodoData)
}
const handleCompleteChange = (id) => {
let newTodoData = todoData.map(data => {
if(data.id === id) {
data.completed = !data.completed; // 반대로
}
return data;
})
setTodoData(newTodoData);
}
return (
<div>
{todoData.map((data) => (
<div style={getStyle(data.completed)} key={data.id}>
<input type="checkbox"
defaultChecked={false}
onChange={() => handleCompleteChange(data.id)} />
{data.title}
<button style={btnStyle}
onClick={() => handleClick(data.id)}>x</button>
</div>
))}
</div>
)
}
구조 분해 할당(Destructuring)
구조 분해 할당이란 ? (ES6)
-> 배열이나 객체의 속성을 해체하여 그 값을 개별 변수에 담을 수 있게 하는 Javascript 표현식.
이를 객체 구조 분해 할당을 사용해 다음과 같이 깔끔하게 가져와줄 수 있다.
아래의 그림을 보면 person 안에 address가 있고, address 안에 zipcode, street, number이 들어간 것을 볼 수 있다.
위와 같이 코드를 길게 작성하기 보다는 배열 구조 분해 할당을 활용해 아래왁 같이 작성해주면 Clear한 코드를 작성할 수 있다.
아래의 코드도 살펴보자.
아래의 for문 안을 보면, name을 n으로 가져올 수 있고, family 안의 father을 f로 가져올 수 있게끔 배열 구조 분해 할당을 해놓았다.
그렇게 console.log를 통해 출력되는 값을 보면 잘 가져오는 것을 확인 할 수 있다.
Form 부분을 위한 컴포넌트 생성하기
App.js에서 List.js로 필요한 부분을 가져와 컴포넌트를 분리한 것 처럼, 마찬가지로 Form.js에 필요한 부분을 가져와 분리해준다.
Form.js를 위와 같이 생성해준다.
그리고 App.js에서 Form.js를 import 해오기 위해 App.js 코드의 상단에 다음과 같이 작성한다.
import Form from "./components/Form";
App.js에서 아래의 코드 부분을 Form.js로 가져온다.
<form style={{ display: 'flex'}} onSubmit={handleSubmit}>
<input
type='text'
name="value"
style={{ flex: '10', padding: '5px'}}
placeholder="해야 할 일을 입력하세요."
value={value}
onChange={handleChange}
/>
<input
type="submit"
value="입력"
className="btn"
style={{flex: '1'}}
/>
</form>
App.js에서 handleChange 함수를 Form.js로 가져온다.
const handleChange = (e) => {
setValue(e.target.value)
}
handleSubmit 함수와 value state, setValue state를 Props로 내려준다.
return(
<div className="container">
<div className="todoBlock">
<div className="title">
<h1>할 일 목록</h1>
</div>
<List todoData={todoData} setTodoData={setTodoData} />
<Form value={value} setValue={setValue} /> // 이렇게 말이다.
그리고 이 Props로 필요한 데이터 함수, 즉 value와 setValue를 가져와준다.
export default function Form({value, setValue}) {
handleSubmit 함수도 가져와야하는데, 이 함수 자체를 Form.js에 가져오기 보다는,
const handleSubmit = (e) => {
e.preventDefault(); //페이지가 리로드 되는 것을 막아줌.
//새로운 할 일 데이터
let newTodo = {
id: Date.now(),
title: value,
completed: false
}
//원래 있던 할 일에 새로운 할 일 더해주기
setTodoData(prev => [...prev, newTodo]);
setValue("");
}
코드를 보면 newTodo state도 'setTodoData(prev => [...prev, newTodo]);'이 안에 있기 때문에 App.js 컴포넌트 안에서 처리해주는 것이 더 좋다.
따라서 Props로 내려주는 것이 더 좋다.
<Form handleSubmit={handleSubmit} value={value} setValue={setValue} />
그리고 이 Props로 필요한 데이터 함수, 즉 handleSubmit을 가져와준다.
export default function Form({handleSubmit, value, setValue}) {