React 할 일 목록 만들기 ( TodoList ) 추가,수정,삭제,검색
실습은 npx react-creat-app [projectname] 으로 실습환경 구축
기능은 추가, 수정, 삭제, 검색을 만들도록 하겠습니다.
프로젝트 구조
실습코드
app.js
import React, { Component } from 'react';
import ToDoForm from './component/ToDoForm';
import ToDoList from './component/ToDoList';
import './App.css';
class App extends Component {
id = 4;
state = {
toDoList: [
{
id: 1,
text: '러닝 뛰기',
},
{
id: 2,
text: '공부하기',
},
{
id: 3,
text: '독서하기',
},
],
search: '',
};
handleCreate = () => {};
handleUpdate = (id, data) => {};
handleRemove = (id) => {};
render() {
const { toDoList } = this.state;
return (
<div>
<ToDoForm onCreate={this.handleCreate} />
<ToDoList data={toDoList} onUpdate={this.handleUpdate} onRemove={this.handleRemove} />
</div>
);
}
}
export default App;
ToDoForm 컴포넌트는 할일 목록을 입력받아 toDoList 배열에 추가할 props handleCreate를 전달한다.
ToDoList는 할일 목록을 출력해주는 컴포넌트로 props로 toDoList, handleUpdate, handleRemove 를 컴포넌트에 전달한다.
우선 ToDoList 컴포넌트를 작성하여 미리 작성된 배열 오브젝트들을 보여주도록 하겠습니다.
ToDoList.js
import React, { Component } from 'react';
import ToDoInfo from './ToDoInfo';
class ToDoList extends Component {
state = {
style: {
border: '1px solid black',
padding: '25px',
margin: '15px',
},
};
render() {
const { data, onUpdate, onRemove } = this.props;
return (
<div>
<ul>
{data.map((data) => (
<li style={this.state.style}>
<ToDoInfo data={data} onUpdate={onUpdate} onRemove={onRemove} />
</li>
))}
</ul>
</div>
);
}
}
export default ToDoList;
props로 전달받은 data를 가져와 map 메소드를 사용하여 li를 출력해주도록 합니다.
여기서 li에 ToDoInfo 컴포넌트를 해준 이유는 map으로 각각의 배열을 출력할 때 해당하는 정보를 ToDoInfo 컴포넌트로 전달하여 보여주기 위함입니다.
ToDoInfo.js
import React, { Component } from 'react';
class ToDoInfo extends Component {
render() {
const { data, onUpdate, onRemove } = this.props;
return (
<div>
<span>{data.text}</span>
</div>
);
}
}
export default ToDoInfo;
여기까지 작성했다면 아래와 같이 결과가 출력될 것 입니다.
이제 여기서 각각의 ToDoInfo에 수정과 삭제를 위해 버튼을 하나 생성하고 거기에 맞는 핸들 메소드를 작성하도록 하겠습니다.
수정
import React, { Component } from 'react';
class ToDoInfo extends Component {
state = {
toggle: false,
text : '',
style : {
margin: '10px',
},
};
handleChange = ( e ) => {
this.setState({
[e.target.name]: e.target.value,
});
};
handleToggleChange = () => {
const { toggle, text } = this.state;
const { data, onUpdate } = this.props;
// false -> true
if (!toggle) {
this.setState({
text : data.text,
toggle: true,
});
} else {
onUpdate(data.id, { text: text });
this.setState({
toggle: false,
});
}
// ture -> false
};
handleRemove = () => {};
render() {
const { data } = this.props;
const { toggle, text } = this.state;
console.log(data.text + ' 렌더링 됐어유');
return (
<div>
{toggle ? (
<input
style={this.state.style}
onChange={this.handleChange}
value={text}
name="text"
></input>
) : (
<span style={this.state.style}>{data.text}</span>
)}
<button onClick={this.handleToggleChange}>{toggle ? '적용' : '수정'}</button>
<button onClick={this.handleRemove}>삭제</button>
</div>
);
}
}
export default ToDoInfo;
toggle 을 만들어 false와 true일 때 handleToggleChange 메소드를 이용하여 각각에 맞는 이벤트들을 처리하도록 했습니다.
handleToggleChange() 메소드에서는 필요한게 두 가지 입니다.
toggle이 false -> true 일 때와 ture -> false일 때 입니다.
false -> ture : 수정 버튼을 눌렀을 경우 input 창 value에 기존에 입력되었던 data.text값을 가져온다
ture => false : 적용 버튼을 누른 경우로 업데이트를 위해 App.js 컴포넌트의 handleUpdate로 보낸다
// App.js 에서의 handleUpdate 부분
handleUpdate = (id, data) => {
console.log(id);
console.log(data);
};
이렇게 바뀐 값들이 handleUpdate 로 넘오오게 된다.
App.js / handleUpdate 부분
handleUpdate = (id, data) => {
const { toDoList } = this.state;
this.setState({
toDoList: toDoList.map((toDoList) => {
console.log(toDoList);
if (toDoList.id === id) {
console.log(toDoList.id + ' / ' + id);
return {
id,
...data,
};
}
return toDoList;
}),
});
};
map 메소드를 사용하여 id값이 서로 같은 경우 수정하려는 배열이 맞으므로 거기만 새로 받아온 data 를 반환해준다.
삭제
ToDoInfo.js / handleRemove 부분
handleRemove = () => {
const { data, onRemove } = this.props;
onRemove(data.id);
};
App.js / handleRemove 부분
handleRemove = (id) => {
const { toDoList } = this.state;
this.setState({
toDoList: toDoList.filter((data) => data.id !== id),
});
};
삭제는 비교적 간단합니다. id값을 가져와 filter 메소드로 받아온 id값과 틀린 것만 toDoList에 반환시키고 id값이 같을 경우 반환시키지 않으면 됩니다.
추가
ToDoForm.js
import React, { Component } from 'react';
class ToDoForm extends Component {
state = {
text: '',
};
handleChange = (e) => {
this.setState({
[e.target.name]: e.target.value,
});
};
handleSubmit = (e) => {
e.preventDefault();
this.props.onCreate(this.state);
this.setState({
text: '',
});
};
render() {
const { text } = this.state;
return (
<div>
<form onSubmit={this.handleSubmit}>
<input value={text} name="text" placeholder="..입력" onChange={this.handleChange}></input>
<button type="submit">추가</button>
</form>
</div>
);
}
}
export default ToDoForm;
input onChange이벤트로 입력받은 값을 state 에 저장하고 submit 이벤트 발생시 handleSubmit에서는 this.props.onCreate(this.state)로 현재의 state 값을 부모 컴포넌트로 값을 보낸다.
App.js / handleCreate 부분
handleCreate = (data) => {
console.log(data);
};
리스트에 추가하려는 데이터가 console창에서 확인결과 잘 받아오고 있다.
이제 받아온 값을 기존의 toDoList 배열에 추가만 해주면 된다.
handleCreate = (data) => {
const { toDoList } = this.state;
this.setState({
toDoList: toDoList.concat({
id: this.id++,
...data,
}),
});
};
concat을 사용하여 기존에 있던 toDoList배열에 추가하면 끝입니다.
검색
검색은 간단히 indexOf을 사용하여 ToDoList에 해당하는 값만 전달하여 검색하는 방식으로 하면 간단하게 구현할 수 있습니다.
App.js / handleSearch 부분
handleSearch = (e) => {
this.setState({
[e.target.name]: e.target.value,
});
};
App.js / render 부분
render() {
const { toDoList, search } = this.state;
return (
<div>
<ToDoForm onCreate={this.handleCreate} />
<input value={search} name="search" onChange={this.handleSearch} placeholder=" ..검색" />
<ToDoList
data={toDoList.filter((data) => data.text.indexOf(search) !== -1)}
onUpdate={this.handleUpdate}
onRemove={this.handleRemove}
/>
</div>
);
}
input에 onChange 를 사용해서 state에 있는 search 값을 저장해주도록했습니다.
그리고 ToDoList 컴포넌트에 data props를 전달해 줄 때 filter 메소드를 사용하여 text에 검색하려는 단어가 있을 경우에만 배열을 반환하여 ToDoList 컴포넌트에 전달하도록 했습니다.