분류 Reactjs

알아야 할 React 앱을 위한 8 가지 유용한 방법

컨텐츠 정보

  • 조회 340 (작성일 )

본문

React는 팬들을 놀라게 하지 않는 단계적으로 많은 변화를 겪었습니다.


처음에는 인터페이스를 만들고 관리하기 위한 믹스인이 있었고 클래스 구성 요소의 개념이 생겼으며 이제는 React 후크를 통해 React 방식으로 앱을 구축하는 방식이 바뀌었습니다.


다른 게 뭔지 알아요? React에 따라 할 수 있는 몇 가지 깔끔한 요령을 아는 것은 앱을 더 잘 구축하는 데 도움이 됩니다 (물론 무언가를 발견 한 경우 당연히 할 수 있는 것을 알 수 없음).


이 기사는 모든 React 개발자가 알아야 할 React에 8 가지 깔끔한 트릭을 다룰 것입니다. 이제 이 목록의 모든 단일 항목이 당신에게 새로운 것이 아니라고 생각하지만 이 목록에서 적어도 하나 이상의 항목이 당신에게 지금까지 할 수 없었던 것을 알기를 바랍니다.


https://dev.to/jsmanifest/8-useful-practices-for-react-apps-you-should-know-5k9 


다음은 알아야 할 8 가지 트릭입니다.


1. 문자열로 React 요소 만들기 


이 목록의 첫 번째 항목은 HTML DOM 요소 태그를 나타내는 간단한 문자열로 일반 React DOM 요소를 만드는 것입니다. 보다 정확하게는 DOM 요소를 나타내는 문자열입니다.


예를 들어 문자열 'div'를 변수에 다음과 같이 지정하여 React 구성 요소를 만들 수 있습니다.


import React from 'react'

const MyComponent = 'div'

function App() {
  return (
    <div>
      <h1>Hello</h1>
      <hr />
      <MyComponent>
        <h3>I am inside a {'<div />'} element</h3>
      </MyComponent>
    </div>
  )
}


React는 React.createElement를 호출하고 해당 문자열을 사용하여 요소를 내부적으로 만듭니다. 깔끔하지 않습니까?


Material-UI와 같은 컴포넌트 라이브러리에서 일반적으로 사용되는 호출자는 컴포넌트의 루트 노드가 props.component의 값이 되도록 결정할 수 있는 컴포넌트 prop를 선언 할 수 있습니다.


function MyComponent({ component: Component = 'div', name, age, email }) {
  return (
    <Component>
      <h1>Hi {name}</h1>
      <div>
        <h6>You are {age} years old</h6>
        <small>Your email is {email}</small>
      </div>
    </Component>
  )
}

이것은 당신이 그것을 사용할 수 있는 방법입니다 :


function App() {
  return (
    <div>
      <MyComponent component="div" name="George" age={16} email="george@gmail.com">
    </div>
  )
}

루트 노드로 사용될 사용자 정의 컴포넌트를 전달할 수도 있습니다.


function Dashboard({ children }) {
  return (
    <div style={{ padding: '25px 12px' }}>
      {children}
    </div>
  )
}

function App() {
  return (
    <div>
      <MyComponent component={Dashboard} name="George" age={16} email="george@gmail.com">
    </div>
  )
}


2. 오류 경계선 사용 


JavaScript에서는 try/catch를 사용하여 코드를 실행하는 동안 발생하는 대부분의 오류를 처리하는 데 사용됩니다. 이러한 오류가 catch 블록에서 발견되면 응용 프로그램이 코드 경계 내에서 충돌하지 않도록 저장할 수 있습니다.


이에 대한 예는 다음과 같습니다.


function getFromLocalStorage(key, value) {
  try {
    const data = window.localStorage.get(key)
    return JSON.parse(data)
  } catch (error) {
    console.error
  }
}

React는 궁극적으로 JavaScript 일 뿐이므로 동일한 전략을 사용하여 오류를 포착하고 처리 할 수 ​​있다고 가정 할 수 있습니다. 그러나 React의 특성으로 인해 구성 요소 내부의 JavaScript 오류는 React의 내부 상태를 손상 시키고 향후 렌더링에서 암호 오류를 발생 시킵니다.


이러한 이유로 React 팀은 오류 경계를 도입했으며 모든 React 개발자는 React 경계 앱에 사용할 수 있도록 이에 대해 알아야 합니다.


오류 경계 이전에 발생하는 오류의 문제점은 이전 렌더링에서 발생한 이후에 이러한 렌더링 오류가 향후 렌더링에서 생성 될 때 React이 컴포넌트에서 오류를 처리하거나 복구 할 수 있는 방법을 제공하지 않았다는 것입니다. 

이것이 우리 모두에게 오류 경계가 필요한 이유입니다!


오류 경계는 구성 요소 트리의 어느 곳에서나 오류를 잡아서 기록하며 충돌 한 구성 요소 트리 대신 대체 UI를 표시 할 수있는 React 구성 요소입니다. 렌더링 중, 수명 주기 메소드 내부 및 그 아래에 있는 전체 트리의 생성자 내부에서 오류가 발생합니다 (이는 앱의 최상위에 선언하고 렌더링 하는 이유입니다).


다음은 React 문서의 예입니다.


class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props)
    this.state = { hasError: false }
  }

  static getDerivedStateFromError(error) {
    // Update state so the next render will show the fallback UI.
    return { hasError: true }
  }

  componentDidCatch(error, errorInfo) {
    // You can also log the error to an error reporting service
    logErrorToMyService(error, errorInfo)
  }

  render() {
    if (this.state.hasError) {
      // You can render any custom fallback UI
      return <h1>Something went wrong.</h1>
    }

    return this.props.children
  }
}


그런 다음 일반 구성 요소로 사용할 수 있습니다.


<ErrorBoundary>
  <MyWidget />
</ErrorBoundary>


3. 이전 가치 유지 


props 또는 state를 업데이트 하는 동안 React.useRef를 사용하여 이전 값을 유지할 수 있습니다.


예를 들어 항목 배열의 현재 및 이전 변경 사항을 추적하려면 이전 값과 현재 값에 React.useState가 할당 된 React.useRef를 만들 수 있습니다.


function MyComponent() {
  const [names, setNames] = React.useState(['bob'])
  const prevNamesRef = React.useRef([])

  React.useEffect(() => {
    prevNamesRef.current = names
  })

  const prevNames = prevNamesRef.current

  return (
    <div>
      <h4>Current names:</h4>
      <ul>
        {names.map((name) => (
          <li key={name}>{name}</li>
        ))}
      </ul>
      <h4>Previous names:</h4>
      <ul>
        {prevNames.map((prevName) => (
          <li key={prevName}>{prevName}</li>
        ))}
      </ul>
    </div>
  )
}


이것은 컴포넌트 렌더링이 끝난 후 React.useEffect가 실행되기 때문에 작동합니다.


setNames가 호출되면 React.useEffect가 이전 렌더링에서 실행 된 마지막 코드이므로 구성 요소 다시 렌더링 및 prefNamesRef는 이전 이름을 보유합니다. 그리고 useEffect에서 prevNamesRef.current를 다시 할당 했으므로 이전 렌더 단계에서 이름이 마지막으로 할당되었으므로 다음 렌더 단계에서 이전 이름이 됩니다.


4. 유연한 비 테일 값 확인을 위해 React.useRef를 사용하십시오. 


React 후크가 React에 도입되기 전에 DOM에 컴포넌트가 마운트 된 후 데이터 페치와 같은 조작이 발생하도록 하려면 클래스 컴포넌트의 componentDidMount 정적 메소드를 사용했습니다.


React 후크가 React에 도입되기 전에 DOM에 컴포넌트가 마운트 된 후 데이터 페치와 같은 조작이 발생하도록 하려면 클래스 컴포넌트의 componentDidMount 정적 메소드를 사용했습니다.


React 후크가 나왔을 때 클래스 컴포넌트를 사용하는 대신 컴포넌트를 작성하는 가장 보편적인 방법이 되었습니다. 구성 요소를 마운트 해제 한 후 상태를 설정하지 못하도록 구성 요소가 마운트 되었는지 추적하려면 다음과 같이하십시오.


import React from 'react'
import axios from 'axios'

class MyComponent extends React.Component {
  mounted = false

  state = {
    frogs: [],
    error: null,
  }

  componentDidMount() {
    this.mounted = true
  }

  componentWillUnmount() {
    this.mounted = false
  }

  async fetchFrogs = (params) => {
    try {
      const response = await axios.get('https://some-frogs-api.com/v1/', { params })
      if (this.mounted) {
        this.setState({ frogs: response.data.items })
      }
    } catch (error) {
      if (this.mounted) {
        this.setState({ error })
      }
    }
  }

  render() {
    return (
      <div>
        <h4>Frogs:</h4>
        <ul>
        {this.state.frogs.map((frog) => <li key={frog.name}>{frog.name}</li>
        )}
        </ul>
    </div>
    )
  }
}


후크에 React하도록 마이그레이션 한 후 후크에 componentDidMount가 없었으며 마운트 해제 후 발생한 상태 업데이트로 인한 메모리 누수 개념은 여전히 ​​후크로 적용됩니다.


그러나 React 후크를 사용하는 componentDidMount와 유사한 방법은 컴포넌트 렌더링이 완료된 후에 실행되므로 React.useEffect를 사용하는 것입니다. React.useRef를 사용하여 마운트 된 값을 여기에 지정하면 클래스 구성 요소 예제와 동일한 효과를 얻을 수 있습니다.


import React from 'react'
import axios from 'axios'

function MyComponent() {
  const [frogs, setFrogs] = React.useState([])
  const [error, setError] = React.useState(null)
  const mounted = React.useRef(false)

  async function fetchFrogs(params) {
    try {
      const response = await axios.get('https://some-frogs-api.com/v1/', {
        params,
      })
      if (mounted.current) {
        setFrogs(response.data.items)
      }
    } catch (error) {
      if (mounted.current) {
        setError(error)
      }
    }
  }

  React.useEffect(() => {
    mounted.current = true

    return function cleanup() {
      mounted.current = false
    }
  }, [])

  return (
    <div>
      <h4>Frogs:</h4>
      <ul>
        {this.state.frogs.map((frog) => (
          <li key={frog.name}>{frog.name}</li>
        ))}
      </ul>
    </div>
  )
}


다시 렌더링 하지 않고 최신 변경 사항을 추적하는 유용한 사용 사례의 또 다른 예는 다음과 같이 React.useMemo와 함께 사용하는 것입니다 (소스).


function setRef(ref, value) {
  // Using function callback version
  if (typeof ref === 'function') {
    ref(value)
    // Using the React.useRef() version
  } else if (ref) {
    ref.current = value
  }
}

function useForkRef(refA, refB) {
  return React.useMemo(() => {
    if (refA == null && refB == null) {
      return null
    }
    return (refValue) => {
      setRef(refA, refValue)
      setRef(refB, refValue)
    }
  }, [refA, refB])
}

이것은 ref props가 바뀌고 정의되면 새로운 기능을 만듭니다. 즉, react는 이전 포크 참조를 null로, 새 포크 참조를 현재 참조로 호출합니다. 또한 React.useMemo가 사용되므로 RefA 또는 RefB의 Ref Prop가 변경 될 때까지 Refs가 메모됩니다.


5. 다른 요소에 의존하는 요소를 사용자 정의하려면 React.useRef를 사용하십시오.


React.useRef에는 노드를 React시키기 위해 ref prop에 자신을 할당하는 것을 포함하여 몇 가지 유용한 사용사례가 있습니다.


function MyComponent() {
  const [position, setPosition] = React.useState({ x: 0, y: 0 })
  const nodeRef = React.useRef()

  React.useEffect(() => {
    const pos = nodeRef.current.getBoundingClientRect()
    setPosition({
      x: pos.x,
      y: pos.y,
    })
  }, [])

  return (
    <div ref={nodeRef}>
      <h2>Hello</h2>
    </div>
  )
}


div 요소의 좌표 위치를 잡으려면 이 예제로 충분합니다. 그러나 앱 어딘가의 다른 요소가 동일한 시간 위치 변경이나 그에 따라 조건 논리를 적용하여 자신의 위치를 ​​업데이트하려는 경우 ref 콜백 함수 패턴을 사용하는 것이 가장 좋습니다. 콜백 함수 패턴을 사용할 때 반응 컴포넌트 인스턴스 또는 HTML DOM 요소를 첫 번째 인수로 받습니다.


아래 예제는 setRef가 ref prop에 적용되는 콜백 함수 인 간단한 예제를 보여줍니다. setRef 내에서 React.useRef 버전을 DOM 요소에 직접 적용하는 대신 필요한 모든 작업을 수행 할 수 있습니다.


const SomeComponent = function({ nodeRef }) {
  const ownRef = React.useRef()

  function setRef(e) {
    if (e && nodeRef.current) {
      const codeElementBounds = nodeRef.current.getBoundingClientRect()
      // Log the <pre> element's position + size
      console.log(`Code element's bounds: ${JSON.stringify(codeElementBounds)}`)
      ownRef.current = e
    }
  }

  return (
    <div
      ref={setRef}
      style={{ width: '100%', height: 100, background: 'green' }}
    />
  )
}

function App() {
  const [items, setItems] = React.useState([])
  const nodeRef = React.useRef()

  const addItems = React.useCallback(() => {
    const itemNum = items.length
    setItems((prevItems) => [
      ...prevItems,
      {
        [`item${itemNum}`]: `I am item # ${itemNum}'`,
      },
    ])
  }, [items, setItems])

  return (
    <div style={{ border: '1px solid teal', width: 500, margin: 'auto' }}>
      <button type="button" onClick={addItems}>
        Add Item
      </button>
      <SomeComponent nodeRef={nodeRef} />
      <div ref={nodeRef}>
        <pre>
          <code>{JSON.stringify(items, null, 2)}</code>
        </pre>
      </div>
    </div>
  )
}


6. 고차 부품 (Higher Order Components) 


강력한 재사용 가능 함수를 작성하기 위한 일반 JavaScript의 공통 패턴은 고차 함수입니다. react는 궁극적으로 JavaScript이므로 react 내에서 고차 함수를 사용할 수도 있습니다.


재사용 가능한 구성 요소의 경우 비결은 고차 구성 요소를 사용하는 것입니다.


고차 컴포넌트는 컴포넌트를 인수로 사용하여 컴포넌트를 리턴 하는 함수가 있는 경우입니다. 애플리케이션에서 로직을 추상화하고 다른 기능과 공유하기 위해 고차 함수를 사용하는 방법과 마찬가지로, 고차 컴포넌트는 컴포넌트에서 로직을 추상화 하여 다른 컴포넌트와 공유 할 수 있게 해줍니다. 즉, 재사용 가능한 많은 컴포넌트를 사용하여 애플리케이션 전체에서 재사용 할 수 있습니다.


다음은 고차 부품의 예입니다. 이 스니펫에서 Border가있는 상위 컴포넌트는 사용자 컴포넌트를 가져와 숨겨진 "중간 레이어" 컴포넌트를 리턴합니다. 그런 다음 부모가 반환 된이 상위 구성 요소를 렌더링 하기로 결정하면 구성 요소로 호출되고 "중간 계층 구성 요소"에서 전달 된 소품을 받습니다.


import React from 'react'

// Higher order component
const withBorder = (Component, customStyle) => {
  class WithBorder extends React.Component {
    render() {
      const style = {
        border: this.props.customStyle
          ? this.props.customStyle.border
          : '3px solid teal',
      }
      return <Component style={style} {...this.props} />
    }
  }

  return WithBorder
}

function MyComponent({ style, ...rest }) {
  return (
    <div style={style} {...rest}>
      <h2>This is my component and I am expecting some styles.</h2>
    </div>
  )
}

export default withBorder(MyComponent, {
  border: '4px solid teal',
})


7. Render Props 


React 라이브러리에서 사용하는 가장 좋아하는 트릭 중 하나는 렌더링 Props 패턴입니다. 여러 구성 요소간에 코드를 공유하는 것과 비슷한 문제를 해결한다는 점에서 상위 구성 요소와 유사합니다. 렌더 Props은 외부 세계에서 자식을 렌더링하는 데 필요한 모든 것을 전달하는 것이 목적인 함수를 제공합니다.


컴포넌트를 React에서 렌더링 하는 가장 기본적인 방법은 다음과 같이 렌더링 하는 것입니다.


function MyComponent() {
  return <p>My component</p>
}

function App() {
  const [fetching, setFetching] = React.useState(false)
  const [fetched, setFetched] = React.useState(false)
  const [fetchError, setFetchError] = React.useState(null)
  const [frogs, setFrogs] = React.useState([])

  React.useEffect(() => {
    setFetching(true)
    api
      .fetchFrogs({ limit: 1000 })
      .then((result) => {
        setFrogs(result.data.items)
        setFetched(true)
        setFetching(false)
      })
      .catch((error) => {
        setError(error)
        setFetching(false)
      })
  }, [])

  return (
    <MyComponent
      fetching={fetching}
      fetched={fetched}
      fetchError={fetchError}
      frogs={frogs}
    />
  )
}

렌더 Props을 사용하면 자식을 렌더링 하는 Props은 다음과 같이 규칙적으로 렌더링이라고 합니다.


function MyComponent({ render }) {
  const [fetching, setFetching] = React.useState(false)
  const [fetched, setFetched] = React.useState(false)
  const [fetchError, setFetchError] = React.useState(null)
  const [frogs, setFrogs] = React.useState([])

  React.useEffect(() => {
    setFetching(true)
    api
      .fetchFrogs({ limit: 1000 })
      .then((result) => {
        setFrogs(result.data.items)
        setFetched(true)
        setFetching(false)
      })
      .catch((error) => {
        setError(error)
        setFetching(false)
      })
  }, [])

  return render({
    fetching,
    fetched,
    fetchError,
    frogs,
  })
}


이 예에서 MyComponent는 렌더 소품 구성 요소라고 하는 구성 요소의 예입니다. 렌더 소품으로 렌더링을 기대하고 자식을 렌더링 하기 위해 호출하기 때문입니다. 이는 렌더 콜백을 통해 공유 상태 및 데이터를 인수로 전달하여 구성 요소를 여러 구성 요소에서 렌더링하고 재사용 할 수 있도록 하는 강력한 패턴입니다.


function App() {
  return (
    <MyComponent
      render={({ fetching, fetched, fetchError, frogs }) => (
        <div>
          {fetching
            ? 'Fetching frogs...'
            : fetched
            ? 'The frogs have been fetched!'
            : fetchError
            ? `An error occurred while fetching the list of frogs: ${fetchError.message}`
            : null}
          <hr />
          <ul
            style={{
              padding: 12,
            }}
          >
            {frogs.map((frog) => (
              <li key={frog.name}>
                <div>Frog's name: {frog.name}</div>
                <div>Frog's age: {frog.age}</div>
                <div>Frog's gender: {frog.gender}</div>
              </li>
            ))}
          </ul>
        </div>
      )}
    />
  )
}


8. Memoize 


React 개발자로 알아야 할 가장 중요한 것 중 하나는 React.memo와 같은 구성 요소의 성능을 최적화 하는 것입니다. 이를 통해 앱이 실행되는 동안 치명적인 충돌을 일으키는 무한 루프와 같은 불쾌한 오류를 방지 할 수 있습니다.


아래의 React 앱에서 메모를 적용 할 수 있는 몇 가지 방법 중 일부에 대해 읽으십시오.



결론 


이것으로 이 포스트의 끝이 끝납니다! 나는 이것이 귀중한 것으로 나타 났으며 앞으로 더 많은 것을 기대하기를 바랍니다!