HOW-TO GUIDES

ant design에서 여러 개의 input state를 관리하는 방법(in typescript react)

encredible 2021. 8. 15. 07:10

이 글은 벨로퍼트와 함께하는 모던 리액트 9. 여러개의 input 상태 관리하기 를 ant design 버전으로 변경시킨 것입니다. 추가적으로 타입스크립트도 함께 적용되어 있는데 타입 스크립트를 사용하지 않는다면 그 부분은 없다고 생각하고 보시면 됩니다.

ant design은 form에 자체 component를 사용하고 event도 다른 걸 쓰기 때문에 9. 여러개의 input 상태 관리하기 의 방식을 그대로 사용할 수가 없습니다. 그래서 저도 처음에는 각각의 input state를 따로 관리했었다가 고민 끝에 이렇게 바꾸었습니다.

예시는 세팅값을 변경하는 상황이라 보시면 됩니다. 코인 거래에 사용되는 심볼들을 변경하거나 한번에 보여지는 테이블의 크기를 정하거나, 거래 전에 alert를 보낼지 말지 결정하거나 하는 input을 처리하려고 합니다.

전체 코드

import React, { useState } from 'react'
import { Form, InputNumber, Radio, RadioChangeEvent, Switch } from 'antd'

const possibleSymbols = [ "BTC/USDT", "ETH/USDT", "XRP/USDT"]

type Setting = {
  [key: string]: any
}

export default function Setting(): JSX.Element {
  const [setting, setSetting] = useState({
    alertBeforeTrade: true,
    numShowingRows: 20,
    currentSymbol: 'BTC/USDT'
  } as Setting)

    const onRadioChange = (key: string) => {
    return (e: RadioChangeEvent): void => {
      setSetting((prevState) => ({
        ...prevState,
        [key]: e.target.value,
      }))
    }
  }

  const onChange = (key: string) => {
    return (value: any): void => {
      setSetting((prevState) => ({
        ...prevState,
        [key]: value,
      }))
    }
  }

  return (
    <Form>
      <Form.Item label="alertBeforeTrade">
        <Switch checked={setting.alertBeforeTrade} onChange={onChange("alertBeforeTrade")} />
      </Form.Item>
      <Form.Item label={"numShowingRows"}>
        <InputNumber onChange={onChange("numShowingRows")} value={setting.numShowingRows} />
      </Form.Item>
      <Form.Item label="currentSymbol">
        <Radio.Group onChange={onRadioChange("currentSymbol")} value={setting.currentSymbol}>
          {possibleSymbols.map((symbol, idx) => (
            <Radio key={idx} value={symbol}>{symbol}</Radio>
          ))}
        </Radio.Group>
      </Form.Item>
    </Form>
  )
}

import 및 사전 작업

import React, { useState } from 'react'
import { Form, InputNumber, Radio, RadioChangeEvent, Switch } from 'antd'

const possibleSymbols = [ "BTC/USDT", "ETH/USDT", "XRP/USDT"]

type Setting = {
  [key: string]: any
}

보시는 바와 같이 react, antd로 부터 import를 하고 있습니다. 예시로 보여드릴 form은 InputNumber, Radio, Switch입니다. 각각 number, string, boolean 값을 input으로 처리하는 form입니다. type Setting 이 부분이 중요합니다. 나중에 setting[key] 방식으로 사용하려면 이러한 type 정의가 필수입니다.

Input 처리하는 부분

  const [setting, setSetting] = useState({
    alertBeforeTrade: true,
    numShowingRows: 20,
    currentSymbol: 'BTC/USDT'
  } as Setting)

    const onRadioChange = (key: string) => {
    return (e: RadioChangeEvent): void => {
      setSetting((prevState) => ({
        ...prevState,
        [key]: e.target.value,
      }))
    }
  }

  const onChange = (key: string) => {
    return (value: any): void => {
      setSetting((prevState) => ({
        ...prevState,
        [key]: value,
      }))
    }
  }

이 부분이 가장 중요한 input을 처리하는 부분입니다. ant design은 자체 component를 사용하기 때문에 onChange 와 같은 event handler가 기존 form과는 다릅니다.

기존 리액트에 자바스크립트인 경우에는 아래와 같이 사용하면 되었습니다.

  const onChange = (e) => {
    const { value, name } = e.target; // 우선 e.target 에서 name 과 value 를 추출
    setInputs({
      ...inputs, // 기존의 input 객체를 복사한 뒤
      [name]: value // name 키를 가진 값을 value 로 설정
    });
  };

그러나 ant design에서는 이와는 다르게 e.target.name을 사용할 수가 없습니다. 따라서 name에 해당하는 부분을 key라는 변수로 두고 고차함수를 사용하여 onChange 함수를 대신 생성해주었습니다.

주의할 점은 onChange가 각 Form 마다 다를 수 있다는 점인데, Radio의 경우 인자가 RadioChangeEvent이고 InputNumber, Switch의 경우에는 value, 즉 number나 boolean이 오게 됩니다. 따라서 이와 같은 방식을 사용해야 여러 개의 input을 하나의 함수로 처리할 수 있습니다.

JSX 부분

  return (
    <Form>
      <Form.Item label="alertBeforeTrade">
        <Switch checked={setting.alertBeforeTrade} onChange={onChange("alertBeforeTrade")} />
      </Form.Item>
      <Form.Item label={"numShowingRows"}>
        <InputNumber onChange={onChange("numShowingRows")} value={setting.numShowingRows} />
      </Form.Item>
      <Form.Item label="currentSymbol">
        <Radio.Group onChange={onRadioChange("currentSymbol")} value={setting.currentSymbol}>
          {possibleSymbols.map((symbol, idx) => (
            <Radio key={idx} value={symbol}>{symbol}</Radio>
          ))}
        </Radio.Group>
      </Form.Item>
    </Form>
  )

위에서 정의한 함수들을 활용한 모습입니다. onChange에 key를 넣어 prop으로 전달하고 있습니다. 이 방식을 사용하면 세팅값이 아무리 늘어나도 함수는 따로 만들지 않아도 됩니다.

Advanced Tip

대체로 이런 기능은 admin 페이지를 만들 때 사용하게 됩니다. 이 때 쓸 수 있는 좋은 Tip으로는 Form.Item을 하나 하나씩 추가하는 것이 아니라 필요한 정보를 Array로 만들어서 Form.Item을 생성한다면 Array에 내용만 추가하면 JSX 부분 수정 없이도 세팅을 확장할 수 있습니다.