내 데이터는 어디로 갔습니까? {반응 라우터}

9009 단어 webdevreactjavascript
이 게시물에서는 이해하는 데 약간의 시간이 걸리는 디버깅 및 문제 해결이 필요한 React Router를 사용하는 방법을 배우면서 처리한 복잡한 문제에 대해 이야기할 것입니다.

단계를 설정하기 위해 동료 개발자와 나는 클라이언트 측 라우팅을 활용하여 각 경로로 DOM에 다른 '페이지'를 렌더링하는 단일 페이지 애플리케이션을 구축하는 임무를 받았습니다.

우리는 Pokémon 라이브러리를 하나의 경로로 렌더링하고 개인 Pokédex를 다른 경로로 렌더링하기 위해 무료Pokémon API에 액세스하는 애플리케이션을 구축했습니다. 라이브러리는 API에서 처음 386개의 포켓몬을 나열하고 Pokédex는 사용자가 저장한(또는 잡은 경우) 모든 포켓몬을 나열합니다. 사용자는 포켓몬 중 하나를 클릭하여 해당 포켓몬의 상세 페이지로 링크 및 라우팅할 수도 있습니다. 각 경로에는 특정 포켓몬에 대한 각 경로를 포함하여 고유한 URL이 있습니다(예: Pikachu를 클릭하면 URL이 localhost:####/pikachu로 변경됨).

URL에 Pokémon을 수동으로 입력하여 자세한 Pokémon 페이지로 라우팅을 시도하기 전까지는 모든 것이 잘되었습니다. Pokémon 데이터가 전달되지 않고 페이지에 아무것도 표시되지 않습니다. 그래서 질문이 생겼습니다: 우리 데이터는 어디로 갔나요???

프레임워크를 설명하기 위해:
우리의 상위 앱 구성 요소는 Library, Pokédex 및 Pokémon Page 구성 요소에 대한 라우팅을 보유합니다. 앱에서는 useEffect 후크의 API에서 데이터를 가져오고 해당 데이터를 상태에 저장합니다. 그런 다음 이 데이터를 세 개의 하위 구성 요소 모두에 props으로 전달하여 해당 구성 요소에서 데이터를 렌더링하는 데 사용할 수 있습니다.

function App() {
  ... //fetched data
  return (
    <div>
      <NavBar />
      <Switch>
        <Route exact path ="/">
          <HomePage />
        </Route>
        <Route exact path="/library">
          <Library
            pokemons={pokemons} //passing props to Library
          />
        </Route>
        <Route exact path="/pokedex">
          <Pokedex
            pokemons={pokemons} //passing props to Pokedex
          />
        </Route>
        <Route exact path="/:name">
          <PokemonPage 
            pokemons={pokemons} //passing props to PokemonPage
          />
        </Route>
      </Switch>
    </div>
  );


재미있는 점은 라이브러리 및 Pokédex 구성 요소의 URL을 수동으로 입력하면 예상대로 로드된다는 것입니다. Pokémon Page가 아무 것도 렌더링하지 않는 특정 Pokémon의 URL을 수동으로 입력하는 경우에만 해당됩니다.

문제를 해결하기 위해 부모 및 자식 구성 요소에 여러 디버거를 설정하여 코드가 사용하는 경로를 따르도록 했습니다. 나는 또한 소품의 콘솔 로그를 Pokémon Page 구성 요소에 배치하여 그것이 통과하는지(그렇지 않았는지) 확인했습니다. 또한 특정 위치의 콘솔 로그는 어떤 코드 라인이 충돌하는지 확인했습니다. 그 때 나는 흥미로운 것을 발견했습니다.

중요한 배경 정보:
처음에 App 구성 요소에서 데이터를 가져올 때 두 번의 가져오기가 비동기적으로 발생하도록 했습니다. 이는 API 설정 방식 때문에 필요했습니다. 첫 번째 가져오기는 포켓몬 이름만 제공하므로 두 번째 가져오기를 사용하여 이름을 반복하여 각 포켓몬의 세부 정보를 가져옵니다. 두 번째 가져오기가 시작되기 전에 첫 번째 가져오기가 완료되어야 하므로 이를 달성하기 위해 async and await을 활용했습니다. 이로 인해 상태가 계속 설정되어 Pokémon Page가 한 번에 많은 양의 데이터만 로드하도록 앱과 Pokémon Page 구성 요소를 렌더링하는 사이에 코드가 앞뒤로 이동했습니다.

function App() {
  const getPokemons = async () => {
    const res = await fetch({pokeAPI url})
    const data = await res.json()

    const createPokemonObject = (results) => {
      results.map(async pokemon => {
        const res = await fetch({pokeAPI url by pokemon name})
        const data = await res.json()
        setPokemons(pokemons => [...pokemons, data])
      })
    }

    createPokemonObject(data.results)
  }

  useEffect(() => {
    getPokemons()
  }, [])

  ... //returned JSX
}


이것이 Pokémon Page 구성 요소를 숨막히는 이유는 가져오기 지연으로 인해 구성 요소가 빈 소품으로 먼저 로드되기 때문입니다. 이렇게 하면 정의되지 않은 소스(소품에서 가져옴)로 인해 이미지 요소가 렌더링되지 않고 구성 요소가 모두 렌더링되지 않습니다. 데이터가 기술적으로 나중에 전송된다는 것을 알고 있지만 구성 요소는 그렇지 않습니다. 따라서 처음에 실패했다고 생각한 후에 구성 요소를 다시 렌더링하려고 시도하지 않습니다.

이 문제를 해결하기 위해 생각해낸 해결책은 소품이 정의되지 않은 경우 다른 JSX(예: '로드 중...' 텍스트)를 반환하도록 JSX를 래핑하는 if 문을 적용하고 그렇지 않으면 원래의 세부 페이지 JSX를 반환하는 것이었습니다. 이를 통해 구성 요소가 아무 것도 실패하지 않는다고 생각하도록 속임으로써 페이지가 비동기식으로 가져온 데이터를 계속 렌더링하려고 시도할 수 있으므로 계속 시도하십시오.

if (pokemon === undefined) {
  return <h1>Loading...</h1>
} else {
  return (
    ... //JSX for the detailed page
  )
}

좋은 웹페이지 즐겨찾기