작은 반응 팁 – 사용자 정의 가능한 필터 패널 구성 요소

12800 단어 react
우리는 기본적으로 다수의 테이블로 구성된 애플리케이션을 개발 중입니다. 물론 고객의 삶을 개선하기 위해 이 테이블의 데이터를 필터링하는 기능을 추가하고 싶었습니다.

데이터 테이블의 종류에 따라 날짜, 가격, 이름 또는 시스템에 있는 항목의 ID로 테이블을 필터링할 수 있습니다. 다른 테이블에는 다른 열 집합이 있으므로 다른 필터를 가질 수 있습니다.

우리는 상태를 로컬로 유지하고 새로운 유형의 필터 필드를 추가하는 기능을 제공하는 재사용 가능하고 사용자 정의 가능한 솔루션을 원했습니다.

다음과 같은 직접적인 솔루션을 사용할 수 있습니다.

function FilterPanel(props) {
  ...

  return pug`
    if props.hasDate
      FieldDate(
        value=...
        onChange=...
      )

    if props.hasAmount
      FieldAmount(
        value=...
        onChange=...
      )

    ...
  `
}


그리고 여기에서 볼 수 있듯이 hasDate , hasAmount 와 같은 플래그로 필드의 존재를 제어합니다. 이는 필드의 순서를 변경하려는 경우 유연하지 않습니다. 그런 다음 우리는 필드와 패널을 분리하기로 결정했습니다.

더 나은 솔루션을 찾기 위한 첫 번째 단계는 인터페이스 초안을 작성하여 우리가 사용하려는 방식을 설명하는 것이었습니다. 우리는 다음을 생각해 냈습니다.

FilterPanel(
  values={}
  onApply=(() => {})
)
  FieldGroup
    FieldDate(
      name="dateMin"
    )    

    FieldDate(
      name="dateMax"
    )

  FieldGroup
    FieldAmount(
      name="amountMin"
    )    

    FieldAmount(
      name="amountMax"
    )


여기에서 볼 수 있듯이 패널을 사용할 테이블에 따라 패널을 구성할 수 있습니다.

이러한 필드 간에 논리를 공유하고 필드를 그룹화하려는 경우에 유연하게 만들기 위해 React Context를 사용했습니다.

새롭다면 official docs을 먼저 읽는 것이 좋습니다.

이 구성 요소에 대해 다음 폴더 구조를 만듭니다.

FilterPanel/
  Context/
  FieldDate/
  FieldAmount/
  FieldName/
  atoms.common.js <--- common styled components
  atoms.js
  index.js


컨텍스트 모듈부터 시작하겠습니다.

import { createContext, useContext } from 'react'

const Context = createContext({
  getValue: () => null,
  setValue: () => {},
})
Context.displayName = 'FilterPanelContext'

export const Provider = Context.Provider

export function useFilterPanelContext() {
  return useContext(Context)
}


이것은 컨텍스트 인스턴스와 함께 작동하는 인터페이스인 Provider 구성 요소 및 useFilterPanelContext입니다.

상태 유지는 FilterPanel 구성 요소로 이동했습니다.

function FilterPanel(props) {
  const [values, setValues] = useState(props.values)
  const [wasChanged, setWasChanged] = useState(false)

  const isApplied = !_.isEmpty(props.values)

  function getValue(name) {
    return values[name]
  }

  function setValue(name, value) {
    setWasChanged(true)
    setValues({ ...values, [name]: value })
  }

  function clearValues() {
    setWasChanged(false)
    setValues({})
    props.onApply({})
  }

  function submitValues(event) {
    event.preventDefault()
    setWasChanged(false)
    props.onApply(values)
  }

  const formLogic = {
    getValue,
    setValue,
  }

  return pug`
    form(onSubmit=submitValues)
      Provider(value=formLogic)
        Wrapper
          each child in Children.toArray(props.children)
            Box(mr=1.5)
              = child

          Box(mr=1.2)
            if isApplied && !wasChanged
              Button(
                type="button"
                variant="outlined"
                size="medium"
                onClick=clearValues
              ) Clear

            else
              Button(
                type="submit"
                variant="outlined"
                size="medium"
              ) Filter
  `
}


코드는 최고의 문서입니다. 그리고 더 알고 싶은 장소가 있다면 여기에 몇 가지 설명이 있습니다.

왜 우리는 국지적으로 국가를 유지합니까? 필터가 변경된 직후에 이 필터를 적용하지 않기를 원합니다. "필터"버튼을 클릭해야만 적용됩니다.

추적하는 이유는 무엇입니까wasChanged? 사용자가 필드 값을 변경했는지 알고 싶기 때문에 "지우기"버튼 대신 "필터"버튼을 다시 표시합니다.
Provider가 우리에게 어떤 도움이 됩니까? value 소품으로 전달된 데이터는 이제 useFilterPanelContext 후크를 사용하는 모든 구성 요소에서 사용할 수 있습니다.
Children.toArray(props.children)의 목적은 무엇입니까? 자식을 렌더링하고 몇 가지 추가 논리를 적용하는 방법입니다. 여기에서 각 자식을 Box — 여백 오른쪽을 추가하는 구성요소로 래핑합니다.

마지막으로 중요한 것은 필드 구성 요소입니다. 금액 1을 예로 들겠습니다. 여기있어:

function FilterPanelFieldAmount(props) {
  const { getValue, setValue } = useFilterPanelContext() <---- our hook

  const handleChange = event => setValue(event.target.name, event.target.value)
  const handleClear = () => setValue(props.name, '')

  const value = getValue(props.name)

  const Icon = pug`
    if value
      IconButton(
        variant="icon"
        size="small"
        type="button"
        onClick=handleClear
      )
        Icons.TimesCircle

    else
      IconLabel(for=props.name)
        Icons.AmountFilter
  `

  return pug`
    FieldText(
      size="medium"
      id=props.name
      name=props.name
      value=value
      placeholder=props.placeholder
      onChange=handleChange
      endAdornment=Icon
    )
  `
}


그리고 그게 다야! React Context를 통해 사용자 정의할 수 있는 것을 만드는 것은 정말 좋은 방법입니다. 도움이 되었기를 바라며 제가 놓친 부분이 있으면 알려주세요.

건배!

좋은 웹페이지 즐겨찾기