본문 바로가기

React

[React 기본] 컴포넌트간 props와 이벤트 전달해보기

 

위와같은 걸 만드는데 컴포넌트를 쪼개어 만들어보았다. vue와 비슷한데 조금은 더 간단(?)하게 전달할 수 있었다.
부모 -> 자식으로 props로 데이터를 전달하고,
자식은 props를 변경하고 싶으면 vue의 emit처럼 부모로 이벤트를 전달하여 값을 변경하였다.

 


1. props 전달해보기 (부모 > 자식)


부모 컴포넌트

import React, { Component } from 'react';
import Habit from './habit';

class Habits extends Component {
  state = {
    habits: [
      { 
        name: 'Reading',
        count: 0,
      },
      { 
        name: 'Running',
        count: 0,
      },
      { 
        name: 'Coading',
        count: 0,
      },
    ]
  }
  
  render() {
    return (
      <ul>
        {this.state.habits.map((habit, index) => {
          return <Habit 
            habit={habit} 
            key={index} 
          ></Habit>;
        })}
      </ul>
    );
  }
}

export default Habits;
state 에 보면 habits라는 배열이 있고 그 배열을 ul 안에서 돌려 사용하고 있다.
잘보면 Habit이라는 컴포넌트가 반복되고 있는데 그 안에는 li가 있다.

그리고 그 Habit컴포넌트로 habit이라는 데이터를 각각 보내고 있다. (habit은 아래 내용이 돌면서 들어갈 것)
habit 0번째 ==> {name: 'Reading', count: 0}
habit 1번째 ==>{name: 'Running', count: 0}
habit 2번째 ==>{name: 'Coading', count: 0}

 

 

자식 컴포넌트 Habit

import React, { Component } from 'react';

class Habit extends Component {
  render() {
    const { name, count } = this.props.habit;

    return (
      <li className="habit">
        <span className="habit-name">{name}</span>
        <span className="habit-count">{count}</span>
        {/* 더하기버튼 */}
        <button className="habit-button habit-increase">
          <i className="fas fa-plus-square"></i>
        </button>
        {/* 빼기버튼 */}
        <button className="habit-button habit-decrease">
          <i className="fas fa-minus-square"></i>
        </button>
        {/* 삭제버튼 */}
        <button className="habit-button habit-delete">
          <i className="fas fa-trash"></i>
        </button>
      </li>
    )
  }
}

export default Habit;
render안에 this.props.habit을 찍어보면 위에 부모컴포넌트에서 보냈던 데이터가 순서대로 찍힌다.
0번째 ==> {name: 'Reading', count: 0}
1번째 ==>{name: 'Running', count: 0}
2번째 ==>{name: 'Coading', count: 0}

비구조화 할당으로 name과 count를 불러와 {name} {count} 이렇게 부모에게서 전달받은 데이터를 사용하였다.

 

 


1. 이벤트 전달해보기(자식 > 부모)


부모 컴포넌트

class Habits extends Component {
  state = {
    habits: [
    	// ... 위에 소스와 동일
    ]
  }
  
  handleIncrement = ( habit ) => {};
  handleDecrement = ( habit ) => {};
  handleDelete = (habit) => {};
  
  render() {
    return (
      <ul>
        {this.state.habits.map((habit, index) => {
          return <Habit 
            habit={habit} 
            key={index} 
            onIncrement={this.handleIncrement}
            onDecrement={this.handleDecrement}
            onDelete={this.handleDelete}
          ></Habit>;
        })}
      </ul>
    );
  }
}

export default Habits;
자식에서 props로 onIncrement 이벤트를 실행하면 각각 { } 안에 있는 함수를 실행시키겠다는 뜻이다.
그래서 위에 함수가 선언되어 있고, 받는 인자값은 name과 count가 모두 필요해서 habit으로 해놨다. 

 

 

자식 컴포넌트 Habit

import React, { Component } from 'react';

class Habit extends Component {
  handleIncrement = () => {
    this.props.onIncrement(this.props.habit);
  };

  handleDecrement = () => {
    this.props.onDecrement(this.props.habit);
  };

  handleDelete = () => {
    this.props.onDelete(this.props.habit);
  };

  render() {
    const { name, count } = this.props.habit;

    return (
      <li className="habit">
        <span className="habit-name">{name}</span>
        <span className="habit-count">{count}</span>
        {/* 더하기버튼 */}
        <button className="habit-button habit-increase" onClick={this.handleIncrement}>
          <i className="fas fa-plus-square"></i>
        </button>
        {/* 빼기버튼 */}
        <button className="habit-button habit-decrease" onClick={this.handleDecrement}>
          <i className="fas fa-minus-square"></i>
        </button>
        {/* 삭제버튼 */}
        <button className="habit-button habit-delete" onClick={this.handleDelete}>
          <i className="fas fa-trash"></i>
        </button>
      </li>
    )
  }
}

export default Habit;

 

더하기, 빼기, 삭제버튼을 onClick하면 각각 이벤트가 발생한다. (ex) handleIncrement)
그럼 그 위에 이벤트가 발생되는데 그 이벤트 안에는 아까 부모가 보냈던 props이벤트를 실행하는 구문이 담겨있다.
this.props.이벤트(보낼 인자값)

예를 들어 더하기 버튼을 누르면 자식의 handleIncrement함수를 실행하고,
그 함수안에 있는 this.props.onIncrement(this.props.habit); 가 실행되면
부모에서 인지가 되므로 부모에서 선언했던 onIncrement 함수는부모의 handleIncrement를 실행시키게 될것이다.

 

부모 컴포넌트 이벤트 완성하기

 handleIncrement = ( habit ) => {
    const habits = [...this.state.habits];
    const index = habits.findIndex(v => v.name === habit.name);
    habits[index].count++;

    this.setState({ habits});
  };
각각의 이벤트에 맞는것을 작성하는데,
state를 통으로 복사해서 자식에서 인자값으로 넘겨받은 habit으로 맞는 인덱스를 찾아 데이터를 setState 통으로 업데이트 시켜주는 코드이다. 이렇게 각각에 맞는 이벤트로 state를 업데이트 시켜주어 자식과 데이터와 이벤트를 주고받는것이 가능하다.

 

 

 

 


부모 컴포넌트 전체소스

import React, { Component } from 'react';
import Habit from './habit';

class Habits extends Component {
  state = {
    habits: [
      { 
        name: 'Reading',
        count: 0,
      },
      { 
        name: 'Running',
        count: 0,
      },
      { 
        name: 'Coading',
        count: 0,
      },
    ]
  }
  
  handleIncrement = ( habit ) => {
    const habits = [...this.state.habits];
    const index = habits.findIndex(v => v.name === habit.name);
    habits[index].count++;

    this.setState({ habits});
  };

  handleDecrement = ( habit ) => {
    const habits = [...this.state.habits];
    const index = habits.findIndex(v => v.name === habit.name);
    habits[index].count = habits[index].count > 0 ? habits[index].count - 1: 0;

    this.setState({ habits });
  };

  handleDelete = (habit) => {
    const habits = [...this.state.habits];
    const index = habits.findIndex(v => v.name === habit.name);

    habits.splice(index, 1);
    this.setState({ habits });
  };
  
  render() {
    return (
      <ul>
        {this.state.habits.map((habit, index) => {
          return <Habit 
            habit={habit} 
            key={index} 
            onIncrement={this.handleIncrement}
            onDecrement={this.handleDecrement}
            onDelete={this.handleDelete}
          ></Habit>;
        })}
      </ul>
    );
  }
}

export default Habits;

 

자식 컴포넌트 전체소스

import React, { Component } from 'react';

class Habit extends Component {
  handleIncrement = () => {
    this.props.onIncrement(this.props.habit);
  };

  handleDecrement = () => {
    this.props.onDecrement(this.props.habit);
  };

  handleDelete = () => {
    this.props.onDelete(this.props.habit);
  };

  render() {
    const { name, count } = this.props.habit;

    return (
      <li className="habit">
        <span className="habit-name">{name}</span>
        <span className="habit-count">{count}</span>
        {/* 더하기버튼 */}
        <button className="habit-button habit-increase" onClick={this.handleIncrement}>
          <i className="fas fa-plus-square"></i>
        </button>
        {/* 빼기버튼 */}
        <button className="habit-button habit-decrease" onClick={this.handleDecrement}>
          <i className="fas fa-minus-square"></i>
        </button>
        {/* 삭제버튼 */}
        <button className="habit-button habit-delete" onClick={this.handleDelete}>
          <i className="fas fa-trash"></i>
        </button>
      </li>
    )
  }
}

export default Habit;
반응형