이미지09
Coding Story/REACT

[ React ] 리액트 Input Form 상태 관리, 배열 관리

반응형

 

 

지금까지는 자식 컴포넌트가 부모 컴포넌트에게 단순한 Element 를 return 해주는 데에 한했다.

이번 게시글에선 Input Element 와 이를 Form 으로 감싸 Form 의 상태를 직접 관리하고,

React 에선 배열을 어떻게 쓰는지 간단하게 알아보자.

먼저 UserInput.js 의 소스를 살펴보자.

 

 

 

UserInput.js

import React, {Component} from 'react';

class UserInput extends Component {
    
    //state 에 userName, userGender, phoneNumber 초기화
    state = {
        userName: '',
        userGender: '',
        phoneNumber: ''
    }

    inputChange = (e) => {

        this.setState({

            //key : value
            [e.target.name]: e.target.value
            
        })
    }

    loginUser = (e) => {

        //submit 후 reload 방지
        e.preventDefault();

        //부모에서 넘어온 onCreate 호출해 state 전달
        this.props.onCreate(this.state);

        //state 초기화
        this.setState({
            userName: '',
            userGender: '',
            phoneNumber: ''
          })
    }

    render() {

        return (

            //로그인 버튼을 눌리면 this(현 컴포넌트)의 loginUser 함수를 호출
            <form onSubmit={this.loginUser}> 

                //input 의 값이 수정되면 this(현 컴포넌트)의 inputChange 함수 호출
                <input type='text' name='userName' placeholder='이름을 입력하세요' onChange={this.inputChange} value={this.state.userName}/>
                <input type='text' name='userGender'  placeholder='성별을 입력하세요' onChange={this.inputChange} value={this.state.userGender}/>
                <input type='text' name='phoneNumber'  placeholder='휴대폰번호를 입력하세요' onChange={this.inputChange} value={this.state.phoneNumber}/>

                <button type='submit'>로그인</button>

            </form>
        )
    }
}
export default UserInput;

 

방식은 이전 까지 게시글의 소스들과 비슷하다.

 

 

 

UserInput.js 컴포넌트는 App.js 에게로 이름, 성별, 휴대폰번호의 입력 폼을 return 해주는 데,

이 때 사용되어지는 함수를 살펴보자.

inputChange = (e) => {
    this.setState({
        //key : value
        [e.target.name]: e.target.value    
    })
}

...

render() {
    ...
    <input type='text' name='userName' placeholder='이름을 입력하세요' onChange={this.inputChange} value={this.state.userName}/>
    <input type='text' name='userGender'  placeholder='성별을 입력하세요' onChange={this.inputChange} value={this.state.userGender}/>
    <input type='text' name='phoneNumber'  placeholder='휴대폰번호를 입력하세요' onChange={this.inputChange} value={this.state.phoneNumber}/>
}

 

이름, 성별, 휴대폰번호를 입력할 때 input 내의 값 변경이 감지되어 this 의 inputChange 함수를 호출하게 되는데

 

실시간으로 input 의 값을 state 안에 할당하려 한다.

userName: e.target.value

userGender: e.target.value

phoneNumber: e.target.value

의 형태로 들어가게 하기 위해 input 에 선언해 놓은 name 들 ( userName, userGender, phoneNumber ) 이

 

[e.target.name] 에 적용되 각각의 name 을 가진 key 로 들어가게 된다.

loginUser = (e) => {

    //submit 후 reload 방지
    e.preventDefault();

    //부모에서 넘어온 onCreate 호출해 state 전달
    this.props.onCreate(this.state);

    //state 초기화
    this.setState({
        userName: '',
        userGender: '',
        phoneNumber: ''
    })
}

 

이 loginUser 함수는 로그인 버튼을 클릭했을 때 호출되는 함수인데

 

현 컴포넌트 ( UserInput.js ) 를 호출한 부모 컴포넌트 ( App.js ) 에게 Form 의 data 를 보내주는 부분이다.

 

부모에서 넘어온 onCreate 함수를 호출하기에 state 가 아닌 props 에서 호출하며 state 를 담아 보낸다.

 

 

 

 

App.js

UserInput.js 를 호출한 부모 컴포넌트

import { render } from '@testing-library/react';
import React, { Component } from 'react';
import './App.css';
import UserInput from './UserInput';

class App extends Component {

  //state 에 userInfo 배열 할당
  state = {
    userInfo: []
  }
  
  //자식 컴포넌트에게서 return 받은 data를 userInfo 배열에 넣는 함수
  loginUserHistory = (data) => {
    const {userInfo} = this.state;
    this.setState({
      userInfo: userInfo.concat({
        ...data
      })
    })
  }

  render() {
    
    //state 안의 userInfo 잡아옴
    const {userInfo} = this.state;

    return (
       <div>
          //UserInput 컴포넌트를 호출한 뒤 현 컴포넌트의 loginUserHistory 함수 호출
          <UserInput onCreate={this.loginUserHistory} />
          <div>
              //실시간으로 JSON String 형태로 화면에 뿌려줌
              {JSON.stringify(userInfo)}
          </div>
        </div>
    );
  }
}

export default App;

 

이번 컴포넌트 역시 함수 단위로 설명해 보자면,

render() {
    
    //state 안의 userInfo 잡아옴
    const {userInfo} = this.state;

    return (
         <div>
            //UserInput 컴포넌트를 호출한 뒤 현 컴포넌트의 loginUserHistory 함수 호출
            <UserInput onCreate={this.loginUserHistory} />
            <div>
                //실시간으로 JSON String 형태로 화면에 뿌려줌
                {JSON.stringify(userInfo)}
            </div>
        </div>
    );
}

 

UserInput 컴포넌트를 호출해 그 결과를 return 받고,

 

그와 동시에 현 컴포넌트에서 선언한 loginUserHistory 함수를 호출한다.

현재 loginUserHistory 함수는 자식 컴포넌트에게서 return 받은 data 를 userInfo 배열에 넣어주고 있는데

이를 "const {userInfo} = this.state;" 로 가져와 JSON String 형태로 화면에 뿌려주는 형태이다.

loginUserHistory 함수를 보면

loginUserHistory = (data) => {
    const {userInfo} = this.state;
    this.setState({
        userInfo: userInfo.concat({
            ...data
        })
    })
}

 

parameter 로 받은 data 를 userInfo 배열에 집어넣어주고 있는데,

 

여기서 중요하게 봐야할 부분이 userInfo: userInfo.cancat 부분이다.

우리가 보통 JAVASCRIPT 에서 배열에 데이터를 추가할 때 push 를 사용하곤 했으나

 

리액트에서는 push 를 사용하면 안된다.

리액트는 불변성을 유지해줘야 리액트 자체에서 그때그때 리렌더링 되도록 설계를 할 수 있는데

push 를 해버리면 state 내부의 값을 직접적으로 수정하기때문에 불변성 유지에 위배되기 때문.

때문에 우리는 React 에서는 기존의 배열에 기반해

 

새로운 배열을 만들어내는 concat, map, filter 같은 함수를 사용해야 한다.

 

 

추가로 덧붙이자면 loginUserHistory 함수 안의

userInfo: userInfo.concat({
    ...data
})

 

...data 는 자바스크립트의 전개 연산자이다.

전개 연산자가 생소하다면 여기를 참고하시면 된당.

 

 

 

 

결과화면

 

포스팅 이미지 01

 

 

반응형