[React] input 폼에 대한 노트 on redgoose note

[React] input 폼에 대한 노트

Nest: Development Category: Javascript 2015-04-17

겪었던 문제들

react.js를 처음 접하여 input폼을 다루다보니 아래와같은 오류메세지를 자주보게된다.

Warning: Failed propType: You provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultValue`. Otherwise, set either `onChange` or `readOnly`. Check the render method of `BasicCalculator`.

effec434565487.png

구글링을 해보니 폼에 대해서 다음과 같은 설명을 보게 되었다.

React는 컴포넌트 객체의 상태를 props와 state, 2가지로 구분해서 관리한다. React 문서에 나온 내용에 따르면 props는 컴포넌트를 생성할 때 사용자가 지정하는 일종의 설정 값이다. 이 값은 외부에서 넘겨받아 컴포넌트를 초기화 할 때 사용한다. 한 번 지정하고 나면 이 값은 변경할 수 없다. 물론 JavaScript 언어 특성상 코드 레벨에서 변경은 가능하지만 개발자 도구 콘솔에 경고 메시지가 뜨는 걸 볼 수 있다. 즉, 초기 설정 값을 보관하는 용도인 셈이다.

value값이 존재한다면 변해야하는 값과 변하지 말아야할 값에 대해서 좀더 조작해야할 필요가 있다.

해결법

변화되지 말아야할 값일때

<input type="number" value={this.state.result}/> 이런형태로 동적으로 값을 바꿀 수 있지만 value 속성이 들어가면 onChange이벤트를 넣던지 readOnly 속성을 넣어서 변화를 막아라고 경고를 준다.

<input type="number" value={this.state.result} readOnly/>

이렇게 변화되지 않게 readOnly속성을 넣어주면 더이상 경고가 뜨질 않는다.

변화되어야하는 값일때

변화되어야하는 폼이 들어갈때 단순히 value속성이 들어가면 경고가 뜨면서 이벤트를 걸었을때 정상적으로 작동되지 않는것을 볼 수 있을것이다.
단순히 경고를 회피하려면 아래와 같이 valuedefaultValue속성으로 바꿔주면 된다.

하지만 defaultValue={this.num}과같이 변수값을 주고 이벤트가 일어나면 변수값이 반영이 되지않는 값으로 계산한다. (input에서 다른값으로 고쳤지만 실질적으론 값이 변하지 않았음)
그래서 onChange이벤트속성을 이용하여 값이 변하면 이벤트 메서드를 실행하여 변수값에 대입해줘야한다.

var Foo = React.createClass({
   num : 0
   ,inputChange : function(event) {
      this.num = event.target.value;
   }
   ,render : function() {
      return (
         <input type="number" defaultValue={this.num} onChange={this.inputChange} />
      )
   }
});

테스트 컴포넌트 소스

// Count more plus
var AddCounter = React.createClass({
    incrementCount : function(){
        this.setState({
            count : this.state.count + 1
        });
        this.setProps({
            count : this.props.count + 5
        });
    },
    getInitialState : function(){
        return {
            count : 0
        }
    },
    render: function(){
        return (
            <div className="counter">
                <h1>{this.state.count}</h1>
                <h2>{this.props.count}</h2>
                <button type="button" onClick={this.incrementCount}>Increment</button>
            </div>
        )
    }
});

// Basic Calculator
var BasicCalculator = React.createClass({
    result : 0,
    inputValues : [4,5],
    getInitialState : function()
    {
        return {
            result : this.result,
            result2 : 0
        };
    },
    action : function(method)
    {
        switch(method)
        {
            case 'plus':
                this.result = this.inputValues[0] + this.inputValues[1];
                break;
            case 'subtraction':
                this.result = this.inputValues[0] - this.inputValues[1];
                break;
            case 'multiply':
                this.result = this.inputValues[0] * this.inputValues[1];
                break;
            case 'dividing':
                this.result = this.inputValues[0] - this.inputValues[1];
                break;
        }
        this.setState({
            result : this.result
        });
    },
    setInputvalue : function(key, event)
    {
        var number = parseInt(event.target.value);
        number = (number) ? number : 0;
        this.inputValues[key] = number;
    },
    render : function()
    {
        return (
            <form>
                <fieldset>
                    <legend>기본 계산기</legend>
                    <input type="number" defaultValue={this.inputValues[0]} onChange={this.setInputvalue.bind(null, 0)}/>
                    <input type="number" defaultValue={this.inputValues[1]} onChange={this.setInputvalue.bind(null, 1)}/>
                    <input type="number" value={this.state.result} readOnly />
                </fieldset>
                <nav className="nav text-center">
                    <button type="button" onClick={this.action.bind(this, 'plus')}>더하기</button>
                    &nbsp;
                    <button type="button" onClick={this.action.bind(this, 'subtraction')}>빼기</button>
                    &nbsp;
                    <button type="button" onClick={this.action.bind(this, 'multiply')}>곱하기</button>
                    &nbsp;
                    <button type="button" onClick={this.action.bind(this, 'dividing')}>나누기</button>
                </nav>
            </form>
        );
    }
});

// render
React.render(<AddCounter count={2} />, document.getElementById('add-counter'));
React.render(<BasicCalculator />, document.getElementById('basic-calculator'));