참고 link : 6장. Class형 컴포넌트와 Function형 컴포넌트 (+Hook)
습관관리 프로젝트
- 습관들(Habits)을 추가할 수도 있고 삭제할 수도 있으며
원하는 습관의 횟수를 추가, 감소 할 수 있다.
- 결과 Page
app.jsx
- state
habits
- render
<Navbar/>
<Habits/>
import React, { Component, Fragment } from 'react'; import './app.css'; import Habits from './components/habits'; import Navbar from './components/navbar'; // class App extends Component { //'습관들(habits)'을 state로 설정 state = { habits: [ { id: 1, name: 'Reading', count: 0 }, { id: 2, name: 'Running', count: 0 }, { id: 3, name: 'Coding', count: 0 } ] } /** state의 count를 1씩 증가시키는 함수 (map함수 이용) map() : 현재 state의 habits을 순차적으로 돌며 하위 컴포넌트에서 받은 '습관(habit)'의 id값과 현재 state로 설정된 '습관들(habits)'의 id값을 비교하여 id 값이 같은 경우에(== ' + ' 버튼를 눌렀을 때 전달받은 habit) state의 해당 habit의 count값을 1 더해준다. (아래 코드의 item은 'habits'의 'habit'을 의미 / item이란 변수명은 코더의 마음대로 설정) */ handleIncrement = (habit) => { const copyHabits = this.state.habits.map(item => { if (item.id === habit.id) { return { ...habit, count: habit.count + 1 } } return item; }) this.setState({ habits: copyHabits }) } /** state의 count를 1씩 감소시키는 함수 (map함수 이용) 위의 handleIncrement와 동일한 개념으로 동작하고, 추가로 마이너스가 연산되었을 때 0미만으로 떨어지는 경우 state를 0으로 설정해준다. */ handleDecrement = (habit) => { const copyHabits = this.state.habits.map(item => { if (item.id === habit.id) { const count = habit.count - 1; return { ...habit, count: count < 0 ? 0 : count }; } return item; }) this.setState({ habits: copyHabits }) } /** habit을 삭제하는 함수 (filter함수 이용) filter() : 현재 state의 habits을 순차적으로 돌며 선택된 habit인 경우를 제외하고 남은 habits를 leftHabits에 담고 이를 현재 state값에 적용한다. (== 선택된 habit만 제거한다.) (아래 코드의 item은 'habits'의 'habit'을 의미 / item이란 변수명은 코더의 마음대로 설정) */ handleDelete = (habit) => { const leftHabits = this.state.habits.filter(item => item.id !== habit.id); this.setState({ habits: leftHabits }) } /** habit을 추가하는 함수 현재의 state값을 복사하고(Spread Operator) 추가로 param으로 받은 name을 habit의 name으로 설정하고 추가해준다. id값은 중복을 피해주기 위해 Date.now()함수를 이용하였다. */ handleAdd = (name) => { const habitsCopy = [...this.state.habits, { id: Date.now(), name: name, count: 0 } ] this.setState({ habits: habitsCopy }); } /** habits속 모든 habit의 count를 0으로 설정해주는 함수 */ handleReset = () => { const habits = this.state.habits.map((habit) => { if (habit.count > 0) { return { ...habit, count: 0 }; } return habit; }); this.setState({ habits }) } render() { return ( <Fragment> <Navbar totalCount={ this.state.habits.filter(item => item.count > 0).length } /> <Habits habits={this.state.habits} onIncrement={this.handleIncrement} onDecrement={this.handleDecrement} onDelete={this.handleDelete} onAdd={this.handleAdd} onReset={this.handleReset} /> </Fragment> ) } } // export default App;
navbar.jsx
- props
totalCount
import React, { PureComponent } from 'react'; // class Navbar extends PureComponent { render() { return ( <div className="navbar"> <i className="navbar-logo fas fa-leaf"></i> <span>Habit Tracker</span> <span className="navbar-count">{this.props.totalCount}</span> </div> ); } } // export default Navbar;
habits.jsx
- props
habits
onIncrement / onDecrement / onDelete/ onAdd / onReset
- render
<HabitAddForm/>
<Habit/>
import React, { Component } from 'react'; import Habit from './habit'; import HabitAddForm from './habitAddForm'; import HabitFilter from './habitFilter'; // class Habits extends Component { handleIncrement = (habit) => { this.props.onIncrement(habit); } handleDecrement = (habit) => { this.props.onDecrement(habit); } handleDelete = (habit) => { this.props.onDelete(habit); } handleAdd = (name) => { this.props.onAdd(name); } handleFilter = (name) => { this.props.onFilter(name); } render() { return ( <> <HabitAddForm onAdd={this.handleAdd} /> <HabitFilter onFilter={this.handleFilter} /> <ul> { this.props.habits.map((habit, index) => { return ( <Habit key={habit.id} habit={habit} onIncrement={this.handleIncrement} onDecrement={this.handleDecrement} onDelete={this.handleDelete} /> ) }) } </ul> <button className="habits-reset" onClick={this.props.onReset}> Reset All </button> </> ); } } // export default Habits
habitAddForm.jsx
- props
onAdd
import React, { memo } from 'react'; // const HabitAddForm = memo(props => { const formRef = React.createRef(); const inputRef = React.createRef(); const onSubmit = (e) => { e.preventDefault(); const name = inputRef.current.value; name && props.onAdd(name); //inputRef.current.value = ''; formRef.current.reset(); } // return ( <form ref={formRef} className="add-from" onSubmit={onSubmit}> <input ref={inputRef} type="text" className="add-input" placeholder="Habit" /> <button className="add-button">Add</button> </form> ); }); // export default HabitAddForm;
habit.jsx
- props
habit
onIncrement / onDecrement / onDelete
import React, { memo } from 'react'; // const Habit = memo((props) => { //컴포넌트가 UI상 등록이 되었을 때 : 사용자에게 보여질 때 const componentDidMount = () => { console.log(`habit : ${props.habit.name} mounted`); } //컴포넌트 지우기 전에 호출 const componentWillUnmount = () => { console.log(`habit : ${props.habit.name} will unmount`); } //컴포넌트리시브받을때 const componentWillReceiveProps = () => { console.log("컴포 리시브 받을 때"); } //props 값을 못 받았을 경우에 대비해 defaultProps설정 const defaultProps = { id: 0, name: 'defaultProps', count: 0 } const handleIncrement = () => { props.onIncrement(props.habit); } const handleDecrement = () => { props.onDecrement(props.habit); } const handleDelete = () => { props.onDelete(props.habit); } const { name, count } = props.habit; // return ( <li className="habit"> <span className="habiit-name">{name}</span> <span className="habit-count">{count}</span> <button className="habit-button habit-increase" onClick={handleIncrement} <i className="fas fa-plus-square"></i> </button> <button className="habit-button habit-decrease" onClick={handleDecrement} <i className="fas fa-minus-square"></i> </button> <button className="habit-button habit-delete" onClick={handleDelete} <i className="fas fa-trash"></i> </button> </li> ) }) // export default Habit;