第七章.Class Component Project


📌 ジェネリック構成部品フィーチャー


注意:第六章。クラス構成部品と機能構成部品(+Hook)

🐱 Github


habit-tracker project

📍 プロジェクトの概要(習慣トレーサ)


慣習管理項目

  • 習慣(Habits)を追加または削除できます.
    欲しい習慣の回数を増やしたり減らしたりすることができます.
  • 結果ページ
  • 📝 実習コード

  • app.jsx

  • statehabits
  • 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

  • propstotalCount
  • 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

  • propshabits 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

  • propsonAdd
  • 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

  • propshabit 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;