본문 바로가기

React

이슈 4 react form 버튼 새로고침 막기

반응형

요약 키워드

  • onSubmit={this.handleSubmit}
  • tag="label"

bootstrap, reactstrap의 힘으로 대략적인 UI가 완성되었습니다. 이제 대충 DB 연결하고 대충 CSS 맞추기만 하면 장학금 날먹이다~!~! 하고 시험 입력을 하던 중... 세상은 제 뜻대로 돌아가지 않는다는 걸 다시 한 번 느꼈습니다...

대략 틀이 잡힌 UI.. 문제는 어떤 버튼을 누르던 리다이렉트가 된다?!

form에서 아무 버튼을 누를 때마다 페이지를 새로 불러왔습니다... submit 속성을 준 적도 없고 action을 정의하지도 않았는데!! react 최대 장점인 바뀐 부분만 렌더링조차 무시해버리다니 엄청난 충격!! 이었습니다.... 해결 방법은 간단합니다. form 태그에 onSubmit 이벤트 핸들러를 추가하여 해결해봅시다.

기존 코드

export default class ReserveSeat extends Component {
  render() {
    return (
      <Form className="tab__panel mt-5">
        <SelectSeat name="좌석 예약" />
        <FormGroup row className="mb-5">
          <Col md={{ size: 5, offset: 1 }}>
            <Row noGutters={true}>
              <h4>인적사항</h4>
            </Row>
            <Info type="input" />
          </Col>
          <Caution />
        </FormGroup>
      </Form>
    );
  }
}

수정 코드 1 (20.01.16 수정)

export default class ReserveSeat extends Component {
  constructor(props) {
    super(props);
    this.handleSubmit = this.handleSubmit.bind(this);
  }
  
  handleSubmit(event) {
    event.preventDefault(); 
    // 추가 코드를 작성하여 DB를 제어하거나 state를 변경할 수 있습니다! 
  }
  
  render() { 
    return (
      <Form onSubmit={this.handleSubmit}> 
        ... 
      </Form>
    );
  } 
}

수정 코드 1처럼 함수 선언을 통해 이벤트 핸들러를 만들어줄 경우,

this.handleSubmit = this.handleSubmit.bind(this);

과정을 거쳐 바인딩을 꼭 해줘야 합니다. 이 과정을 거치지 않으면 this에 접근할 수 없고, 아래와 같은 에러가 표시되게 됩니다.

Uncaught TypeError: Cannot read property 'props' of undefined
    at onChange (select_time.js?a702:37)
    at HTMLUnknownElement.callCallback (react-dom.development.js?61bb:336)
    at Object.invokeGuardedCallbackDev (react-dom.development.js?61bb:385)
    at invokeGuardedCallback (react-dom.development.js?61bb:440)
    at invokeGuardedCallbackAndCatchFirstError (react-dom.development.js?61bb:454)
    at executeDispatch (react-dom.development.js?61bb:584)
    at executeDispatchesInOrder (react-dom.development.js?61bb:609)
    at executeDispatchesAndRelease (react-dom.development.js?61bb:713)
    at executeDispatchesAndReleaseTopLevel (react-dom.development.js?61bb:722)
    at Array.forEach (<anonymous>)

함수 표현식으로 코드를 작성하면 번거로운 바인딩 작업을 한 번에 처리할 수 있습니다.

수정 코드 2 (20.01.16 추가)

export default class ReserveSeat extends Component {
  handleSubmit = (event) => {
    event.preventDefault(); 
    // 추가 코드를 작성하여 DB를 제어하거나 state를 변경할 수 있습니다! 
  }
  
  render() { 
    return (
      <Form onSubmit={this.handleSubmit}> 
        ... 
      </Form>
    );
  } 
}

주석 부분에 코드를 추가하면, react의 기능을 활용하면서 새로운 컴포넌트를 추가할 수도, state를 변경할 수도 잇습니다. 하지만, 좌석을 선택하는 부분이 <button> 태그인 이상, form 제출을 수행하는 <button>과 구분을 해줘야 합니다. 저는 이 때 reactstrap의 tag property를 사용했습니다. 아래는 reactstrap doc에서 제공하는 Button의 속성입니다.

Button.propTypes = {
  active: PropTypes.bool,
  'aria-label': PropTypes.string,
  block: PropTypes.bool,
  color: PropTypes.string, // default: 'secondary'
  disabled: PropTypes.bool,
  outline: PropTypes.bool,

  // Pass in a Component to override default button element
  // example: react-router Link
  // default: 'button'
  tag: PropTypes.oneOfType([
    PropTypes.func,
    PropTypes.string,
    PropTypes.shape({ $$typeof: PropTypes.symbol, render: PropTypes.func }),
    PropTypes.arrayOf(PropTypes.oneOfType([
      PropTypes.func,
      PropTypes.string,
      PropTypes.shape({ $$typeof: PropTypes.symbol, render: PropTypes.func }),
    ]))
  ]),

  // ref will only get you a reference to the Button component, use innerRef to get a reference to the DOM element (for things like focus management).
  innerRef: PropTypes.oneOfType([PropTypes.object, PropTypes.func, PropTypes.string]),

  onClick: PropTypes.func,
  size: PropTypes.string,
  children: PropTypes.node,
  className: PropTypes.string,
  cssModule: PropTypes.object,

  // use close prop for BS4 close icon utility
  close: PropTypes.bool,
}

Button.defaultProps = {
  color: 'secondary',
  tag: 'button',
}

기존 좌석 컴포넌트 코드

class Seat extends Component { 
  render() { 
    const { id } = this.props;
    return (
      <Button color="primary" block={true} className="seat-btn">
        <Input type="radio" name="" id={"jb-radio" + id} />
        {id}
      </Button> 
    );
  }
}

위 코드는 아래와 같이 랜더링 됩니다.

수정된 좌석 컴포넌트 코드

class Seat extends Component { 
  render() { 
    const { id } = this.props;
    return (
      <Button color="primary" block={true} className="seat-btn">
        <Input type="radio" name="" id={"jb-radio" + id} />
        {id}
      </Button> 
    );
  }
}

위와 같이 <Button>에 tag="label"을 추가하게 되면 아래와 같이 랜더링 됩니다. 기존 버튼에 적용했던 CSS는 그대로이고 태그만 변경됩니다.

 

출처:
폼 - react: https://ko.reactjs.org/docs/forms.html

 

폼 – React

A JavaScript library for building user interfaces

ko.reactjs.org

reactstrap - Buttons: https://reactstrap.github.io/components/buttons/

 

reactstrap - Buttons

– Custom content and aria-label

reactstrap.github.io

 

반응형