[TIL] onChange 함수 합치기, Menu Tab 구현하기

유휘찬·2020년 8월 22일
1
post-custom-banner

onChange 함수 합치기

class OnChange extends Component {
  render() {
    return (
      <div>  
        email: <input />
        password: <input />
      </div> 
    );
  } 
}

위 예제에는 두개의 인풋이 있다.

사용자가 input 에 입력한 값을 state 저장하려면 어떻게 해야 할까?

class OnChange extends Component {
  constructor() {
    super();
    this.state = {
      Email: "",
      Pw: "",
    }
  }
  
  handleEmail = (e) => {
    console.log("Email!!") // 우선 함수가 잘 동작하는지 확인하자
    this.setState({
      Email: e.target.value,
    })
  }
  
  handlePw = (e) => {
    console.log("Pw!!") // 우선 함수가 잘 동작하는지 확인하자
    this.setState({
      Pw: e.target.value,
    })
  }

  render() {
    console.log(this.state); // 최신화된 state 의 값을 콘솔로 확인해본다
    return (
      <div>  
        email: <input onChange={this.handleEmail} />
        password: <input onChange={this.handlePw} />
      </div> 
    );
  } 
}

각 input 에 onChange 이벤트를 걸고, state 를 setState() 로 최신화 시켜주면 된다.

이 때 render() 안에서 console.log() 를 하는 이유는 setState() 의 비동기처리 때문에 그때그때 최신화 된 값을 확인할 수 없기 때문이다. setState() 가 실행되면 다시 render() 가 실행되기 때문에 render() 는 항상 최신화된 값을 가지고 있다.

이렇게 각각의 input 에 다른 이벤트를 걸어 따로 값을 가져올 수도 있지만, 하나의 메서드로 두개 이상의 input 을 관리하는 효율적인 방법이 있다.

class OnChange extends Component {
  constructor() {
    super();
    this.state = {
      Email: "", // 1번
      Pw: "", 
    }
  }
  
  handleChange = (e) => {
    this.setState({
      [e.target.name]: e.target.value, // 2번
    })
  }

  render() {
    return (
      <div>  
        email: <input 
        name="Email"  // 3번
        onChange={this.handleChange} />
        password: <input 
        name="Pw" 
        onChange={this.handleChange} />
      </div> 
    );
  } 
}

일단 두개의 input 이벤트에 하나의 함수를 걸어준다. 이 때 실행되는 함수는 setState() 함수로 [e.target.name] 이라는 key 에 e.target.value 라는 사용자가 입력한 값을 저장한다.

[e.target.name] 는 계산된 속성명이라고 하며, 여기서는 이벤트가 발생한 대상 즉, input 의 name 속성이 무엇인지에 따라 그에 해당하는 key의 값을 setState() 한다.

email input 에 값을 입력하였으면 그 input 의 name 은 Email 일 것이고, Email 이라는 key 의 값이 사용자가 입력한 값이 되는 것이다.

결과는 같지만 내부적으로 효율성이 증가했다. 이제 name 속성을 활용하면 input 태그가 몇개가 있건 하나의 메서드로 관리할 수 있다.

Menu Tab 구현하기

export default class MenuTab extends Component {
  render() {
    return (
      <div className="wrapper">
        <ul className="tabs">
          <li>First</li>
          <li>Second</li>
          <li>Third</li>
        </ul>
        <div className="contents">
          <First />
        </div>
      </div>
    );
  }
}

우선 위 예제는 아래의 화면을 구성한다. 간단하게 말하자면 First, Second, Third 라는 Tab 을 클릭할 때마다 그에 해당하는 각각의 화면을 사용자에게 보여주고 싶은 상황이다.

우선적으로 사용자가 어떤 Tab 을 클릭했는지 효과적으로 확인할 수 있는 방법은 무엇일까?

export default class MenuTab extends Component {
  clickHandler = (id) => {
    console.log(id)
  };

  render() {
    return (
      <div className="wrapper">
        <ul className="tabs">
          <li onClick={() => this.clickHandler(0)}>First</li>
          <li onClick={() => this.clickHandler(1)}>Second</li>
          <li onClick={() => this.clickHandler(2)}>Third</li>
        </ul>
        <div className="contents">
          <First />
        </div>
      </div>
    );
  }
}

li 마다 고유의 id 주어서 식별할 수도 있지만, 굳이 그럴 필요 없이 이벤트가 발생하면 실행되는 함수에 고유의 인자를 넣어 보내주면된다.

아래에서 각 Tab을 한번씩 클릭했더니 클릭이벤트가 발생하면 실행되는 함수로 넣어 보내주었던 인자값이 콘솔창에 찍히는 것을 확인할 수 있다.

이렇게 함수에 넣어 보내주었던 인자값을 확인했으니 활용할 수가 있다. 그 인자(id) 를 기존 state 에 업데이트 시켜준 후, 업데이트 된 값(0, 1, 2)에 해당하는 key 를 가진 객체를 component 밖에 선언해 주면 객체의 value 에 접근하는 방식을 사용하여 어떤 메뉴탭을 클릭하냐에 따라 다른 컴포넌트를 렌더링 할 수 있게 된다.

const obj = {
  0: <First />,
  1: <Second />,
  2: <Third />,
}

export default class MenuTab extends Component {
  state = {
    activeTab: 0,
  }

  clickHandler = (id) => {
    this.setState({
      activeTab: id,
    })
  };

  render() {
    return (
      <div className="wrapper">
        <ul className="tabs">
          <li onClick={() => this.clickHandler(0)}>First</li>
          <li onClick={() => this.clickHandler(1)}>Second</li>
          <li onClick={() => this.clickHandler(2)}>Third</li>
        </ul>
        <div className="contents">
          {obj[this.state.activeTab]}
        </div>
      </div>
    );
  }
}
profile
tenacity
post-custom-banner

0개의 댓글