React Hooks를 사용한 Stale-while-revalidate 데이터 가져오기: 가이드

게시 됨: 2022-03-11

stale-while-revalidate HTTP Cache-Control 확장을 활용하는 것은 널리 사용되는 기술입니다. 여기에는 캐시에서 발견된 캐시된(부실) 자산을 사용한 다음 캐시를 재검증하고 필요한 경우 자산의 새 버전으로 업데이트하는 작업이 포함됩니다. 따라서 이름이 stale-while-revalidate 입니다.

stale-while-revalidate 작동 방식

요청이 처음 전송되면 브라우저에 의해 캐시됩니다. 그런 다음 동일한 요청이 두 번째로 전송되면 캐시가 먼저 확인됩니다. 해당 요청의 캐시가 사용 가능하고 유효한 경우 캐시가 응답으로 반환됩니다. 그런 다음 캐시에 부실이 있는지 확인하고 부실이 발견되면 업데이트됩니다. 캐시의 부실성은 stale-while-revalidate 와 함께 Cache-Control 헤더에 있는 max-age 값에 의해 결정됩니다.

유효 기간이 지난 논리를 추적하는 순서도입니다. 요청으로 시작됩니다. 캐시되지 않았거나 캐시가 유효하지 않은 경우 요청이 전송되고 응답이 반환되고 캐시가 업데이트됩니다. 그렇지 않으면 캐시된 응답이 반환되고 캐시가 부실한지 확인됩니다. 오래된 경우 요청이 전송되고 캐시가 업데이트됩니다.

캐시된 자산이 더 이상 중요한 경로에 없기 때문에 빠른 페이지 로드가 가능합니다. 즉시 로드됩니다. 또한 개발자가 캐시 사용 및 업데이트 빈도를 제어하기 때문에 브라우저가 사용자에게 지나치게 오래된 데이터를 표시하는 것을 방지할 수 있습니다.

독자들은 서버가 응답에서 특정 헤더를 사용하도록 하고 브라우저가 거기에서 헤더를 가져오도록 할 수 있다면 캐싱을 위해 React와 Hooks를 사용해야 하는 이유가 무엇이라고 생각할 수도 있습니다.

서버와 브라우저 접근 방식은 정적 콘텐츠를 캐시할 때만 잘 작동합니다. 동적 API에 대해 stale-while-revalidate 를 사용하는 것은 어떻습니까? 이 경우 max-agestale-while-revalidate 에 대한 좋은 값을 찾기가 어렵습니다. 종종 캐시를 무효화하고 요청이 전송될 때마다 새로운 응답을 가져오는 것이 최선의 선택이 될 것입니다. 이것은 사실상 캐싱이 전혀 없음을 의미합니다. 하지만 React와 Hooks를 사용하면 더 잘할 수 있습니다.

API에 대한 stale-while-revalidate

우리는 HTTP의 stale-while-revalidate 가 API 호출과 같은 동적 요청과 잘 작동하지 않는다는 것을 알아차렸습니다.

우리가 그것을 사용하게 되더라도 브라우저는 캐시나 새로운 응답 중 하나를 반환할 것이며 둘 다 반환하지 않을 것입니다. 이것은 요청이 전송될 때마다 새로운 응답을 원하기 때문에 API 요청과 잘 어울리지 않습니다. 그러나 새로운 응답을 기다리면 앱의 의미 있는 사용이 지연됩니다.

그래서 우리는 무엇을합니까?

우리는 사용자 정의 캐싱 메커니즘을 구현합니다. 그 안에서 우리는 캐시와 새로운 응답을 모두 반환하는 방법을 찾습니다. UI에서 캐시된 응답은 사용 가능한 경우 새로운 응답으로 대체됩니다. 로직은 다음과 같습니다.

  1. API 서버 엔드포인트에 처음으로 요청이 전송되면 응답을 캐시한 다음 반환합니다.
  2. 다음에 동일한 API 요청이 발생하면 즉시 캐시된 응답을 사용하십시오.
  3. 그런 다음 요청을 비동기적으로 보내 새 응답을 가져옵니다. 응답이 도착하면 비동기적으로 UI에 변경 사항을 전파하고 캐시를 업데이트합니다.

이 접근 방식을 사용하면 모든 API 요청이 캐시되기 때문에 즉각적인 UI 업데이트가 가능하지만 새로운 응답 데이터가 사용 가능한 즉시 표시되기 때문에 UI의 궁극적인 정확성도 가능합니다.

이 자습서에서는 이를 구현하는 방법에 대한 단계별 접근 방식을 볼 것입니다. 새로운 응답을 받으면 UI가 실제로 새로 고쳐 지기 때문에 이 접근 방식 을 stale-while-refresh 라고 부릅니다.

준비: API

이 튜토리얼을 시작하려면 먼저 데이터를 가져올 API가 필요합니다. 운 좋게도 사용 가능한 모의 API 서비스가 많이 있습니다. 이 튜토리얼에서는 reqres.in을 사용할 것입니다.

우리가 가져오는 데이터는 page 쿼리 매개변수가 있는 사용자 목록입니다. 가져오는 코드는 다음과 같습니다.

 fetch("https://reqres.in/api/users?page=2") .then(res => res.json()) .then(json => { console.log(json); });

이 코드를 실행하면 다음과 같은 결과가 나옵니다. 다음은 반복되지 않는 버전입니다.

 { page: 2, per_page: 6, total: 12, total_pages: 2, data: [ { id: 7, email: "[email protected]", first_name: "Michael", last_name: "Lawson", avatar: "https://s3.amazonaws.com/uifaces/faces/twitter/follettkyle/128.jpg" }, // 5 more items ] }

실제 API와 비슷함을 알 수 있습니다. 응답에 페이지 매김이 있습니다. page 쿼리 매개변수는 페이지 변경을 담당하며 데이터 세트에는 총 2개의 페이지가 있습니다.

React 앱에서 API 사용

React 앱에서 API를 사용하는 방법을 살펴보겠습니다. 방법을 알게 되면 캐싱 부분을 알아낼 것입니다. 우리는 클래스를 사용하여 컴포넌트를 생성할 것입니다. 코드는 다음과 같습니다.

 import React from "react"; import PropTypes from "prop-types"; export default class Component extends React.Component { state = { users: [] }; componentDidMount() { this.load(); } load() { fetch(`https://reqres.in/api/users?page=${this.props.page}`) .then(res => res.json()) .then(json => { this.setState({ users: json.data }); }); } componentDidUpdate(prevProps) { if (prevProps.page !== this.props.page) { this.load(); } } render() { const users = this.state.users.map(user => ( <p key={user.id}> <img src={user.avatar} alt={user.first_name} style={{ height: 24, width: 24 }} /> {user.first_name} {user.last_name} </p> )); return <div>{users}</div>; } } Component.propTypes = { page: PropTypes.number.isRequired };

실제 응용 프로그램에서 자주 발생하는 것처럼 props 를 통해 page 값을 가져옵니다. 또한 this.props.page 가 변경될 때마다 API 데이터를 다시 가져오는 componentDidUpdate 함수가 있습니다.

이 시점에서 API는 페이지당 6개의 항목을 반환하므로 6명의 사용자 목록이 표시됩니다.

React 컴포넌트 프로토타입의 미리보기: 이름 왼쪽에 사진이 있는 6개의 중앙 라인.

새로 고침 중 부실 캐싱 추가

여기에 stale-while-refresh 캐싱을 추가하려면 앱 로직을 다음과 같이 업데이트해야 합니다.

  1. 요청의 응답을 처음 가져온 후 고유하게 캐시합니다.
  2. 요청의 캐시가 발견되면 즉시 캐시된 응답을 반환합니다. 그런 다음 요청을 보내고 새로운 응답을 비동기적으로 반환합니다. 또한 다음 시간을 위해 이 응답을 캐시합니다.

캐시를 고유하게 저장하는 전역 CACHE 개체를 사용하여 이를 수행할 수 있습니다. 고유성을 위해 this.props.page 값을 CACHE 개체의 키로 사용할 수 있습니다. 그런 다음 위에서 언급한 알고리즘을 간단히 코딩합니다.

 import apiFetch from "./apiFetch"; const CACHE = {}; export default class Component extends React.Component { state = { users: [] }; componentDidMount() { this.load(); } load() { if (CACHE[this.props.page] !== undefined) { this.setState({ users: CACHE[this.props.page] }); } apiFetch(`https://reqres.in/api/users?page=${this.props.page}`).then( json => { CACHE[this.props.page] = json.data; this.setState({ users: json.data }); } ); } componentDidUpdate(prevProps) { if (prevProps.page !== this.props.page) { this.load(); } } render() { // same render code as above } }

캐시는 발견되는 즉시 반환되고 새로운 응답 데이터도 setState 에 의해 반환되기 때문에 원활한 UI 업데이트가 가능하며 두 번째 요청 이후부터는 앱에서 더 이상 대기 시간이 없습니다. 이것은 완벽하며 간단히 말해서 오래된 새로 고침 방법입니다.

stale-while-refresh 로직을 추적하는 순서도. 요청으로 시작됩니다. 캐시된 경우 캐시된 응답과 함께 setState()가 호출됩니다. 어느 쪽이든 요청이 전송되고 캐시가 설정되며 setState()가 새로운 응답으로 호출됩니다.

여기서 apiFetch 함수는 캐싱의 이점을 실시간으로 확인할 수 있도록 fetch 를 통한 래퍼에 불과합니다. API 요청에서 반환된 users 목록에 임의의 사용자를 추가하여 이를 수행합니다. 또한 무작위 지연을 추가합니다.

 export default async function apiFetch(...args) { await delay(Math.ceil(400 + Math.random() * 300)); const res = await fetch(...args); const json = await res.json(); json.data.push(getFakeUser()); return json; } function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }

여기서 getFakeUser() 함수는 가짜 사용자 객체를 생성하는 역할을 합니다.

이러한 변경으로 API는 이전보다 더 현실적입니다.

  1. 응답에 무작위 지연이 있습니다.
  2. 동일한 요청에 대해 약간 다른 데이터를 반환합니다.

이를 감안할 때 기본 구성 Component 에서 구성 요소로 전달된 page 소품을 변경할 때 API 캐싱이 작동하는 것을 볼 수 있습니다. 이 CodeSandbox에서 몇 초에 한 번씩 토글 버튼을 클릭하면 다음과 같은 동작이 표시됩니다.

캐싱이 활성화된 전환 페이지를 보여주는 애니메이션. 구체적인 내용은 기사에 설명되어 있습니다.

자세히 보면 몇 가지 일이 발생합니다.

  1. 앱이 시작되고 기본 상태가 되면 7명의 사용자 목록이 표시됩니다. 다음에 이 요청이 전송될 때 무작위로 수정될 사용자이기 때문에 목록의 마지막 사용자를 기록해 두십시오.
  2. 처음으로 Toggle을 클릭하면 짧은 시간(400-700ms) 동안 기다렸다가 다음 페이지로 목록을 업데이트합니다.
  3. 이제 두 번째 페이지에 있습니다. 다시 목록의 마지막 사용자를 기록해 둡니다.
  4. 이제 Toggle을 다시 클릭하면 앱이 첫 번째 페이지로 돌아갑니다. 이제 마지막 항목은 여전히 ​​1단계에서 기록해 둔 동일한 사용자이며 나중에 새(임의) 사용자로 변경됩니다. 처음에는 캐시가 표시된 다음 실제 응답이 시작되었기 때문입니다.
  5. Toggle을 다시 클릭합니다. 같은 현상이 발생합니다. 지난 시간의 캐시된 응답이 즉시 로드되고 새 데이터가 가져오기 때문에 3단계에서 기록해 둔 마지막 항목 업데이트가 표시됩니다.

이것이 바로 우리가 찾던 stale-while-refresh 캐싱입니다. 그러나 이 접근 방식은 코드 중복 문제가 있습니다. 캐싱이 있는 다른 데이터 가져오기 구성 요소가 있는 경우 어떻게 되는지 봅시다. 이 구성 요소는 첫 번째 구성 요소와 다르게 항목을 표시합니다.

다른 구성 요소에 Stale-while-refresh 추가

첫 번째 구성 요소에서 논리를 복사하기만 하면 됩니다. 두 번째 구성 요소는 고양이 목록을 보여줍니다.

 const CACHE = {}; export default class Component2 extends React.Component { state = { cats: [] }; componentDidMount() { this.load(); } load() { if (CACHE[this.props.page] !== undefined) { this.setState({ cats: CACHE[this.props.page] }); } apiFetch(`https://reqres.in/api/cats?page=${this.props.page}`).then( json => { CACHE[this.props.page] = json.data; this.setState({ cats: json.data }); } ); } componentDidUpdate(prevProps) { if (prevProps.page !== this.props.page) { this.load(); } } render() { const cats = this.state.cats.map(cat => ( <p key={cat.id} style={{ background: cat.color, padding: "4px", width: 240 }} > {cat.name} (born {cat.year}) </p> )); return <div>{cats}</div>; } }

보시다시피 여기에 관련된 구성 요소 논리는 첫 번째 구성 요소와 거의 동일합니다. 유일한 차이점은 요청된 끝점과 목록 항목을 다르게 표시한다는 것입니다.

이제 이 두 구성 요소를 나란히 표시합니다. 비슷하게 동작하는 것을 볼 수 있습니다.

두 개의 나란히 있는 구성 요소로 전환하는 것을 보여주는 애니메이션입니다.

이 결과를 얻으려면 많은 코드 복제를 수행해야 했습니다. 이와 같은 구성 요소가 여러 개 있으면 코드를 너무 많이 복제하게 됩니다.

중복되지 않는 방식으로 이를 해결하기 위해 데이터를 가져오고 캐싱하고 이를 props로 전달하기 위한 고차 컴포넌트를 가질 수 있습니다. 이상적이지는 않지만 작동할 것입니다. 그러나 단일 구성 요소에서 여러 요청을 수행해야 하는 경우 여러 고차 구성 요소를 갖는 것은 정말 빨리 추악해집니다.

그런 다음 렌더링 소품 패턴이 있습니다. 이는 클래스 구성 요소에서 이를 수행하는 가장 좋은 방법일 것입니다. 완벽하게 작동하지만 다시 "래퍼 지옥"이 되기 쉽고 때때로 현재 컨텍스트를 바인딩해야 합니다. 이것은 훌륭한 개발자 경험이 아니며 좌절과 버그로 이어질 수 있습니다.

이것은 React Hooks가 하루를 저장하는 곳입니다. 이를 통해 재사용 가능한 컨테이너에 구성 요소 논리를 넣어 여러 위치에서 사용할 수 있습니다. React Hooks는 React 16.8에서 도입되었으며 기능 구성 요소에서만 작동합니다. React 캐시 제어, 특히 Hooks로 콘텐츠 캐싱을 하기 전에 먼저 함수 구성 요소에서 간단한 데이터 가져오기를 수행하는 방법을 살펴보겠습니다.

함수 구성 요소에서 API 데이터 가져오기

함수 구성 요소에서 API 데이터를 가져오기 위해 useStateuseEffect 후크를 사용합니다.

useState 는 클래스 구성 요소의 statesetState 와 유사합니다. 우리는 이 후크를 사용하여 함수 구성 요소 내부에 상태의 원자 컨테이너를 갖습니다.

useEffect 는 수명 주기 후크이며 componentDidMount , componentDidUpdatecomponentWillUnmount 의 조합으로 생각할 수 있습니다. useEffect 에 전달된 두 번째 매개변수를 종속성 배열이라고 합니다. 종속성 배열이 변경되면 useEffect 의 첫 번째 인수로 전달된 콜백이 다시 실행됩니다.

다음은 이러한 후크를 사용하여 데이터 가져오기를 구현하는 방법입니다.

 import React, { useState, useEffect } from "react"; export default function Component({ page }) { const [users, setUsers] = useState([]); useEffect(() => { fetch(`https://reqres.in/api/users?page=${page}`) .then(res => res.json()) .then(json => { setUsers(json.data); }); }, [page]); const usersDOM = users.map(user => ( <p key={user.id}> <img src={user.avatar} alt={user.first_name} style={{ height: 24, width: 24 }} /> {user.first_name} {user.last_name} </p> )); return <div>{usersDOM}</div>; }

pageuseEffect 에 대한 종속성으로 지정하여 page 가 변경될 때마다 useEffect 콜백을 실행하도록 React에 지시합니다. 이것은 componentDidUpdate 와 같습니다. 또한 useEffect 는 항상 처음 실행되므로 componentDidMount 처럼 작동합니다.

함수 구성 요소의 부실 새로 고침

useEffect 가 구성 요소 수명 주기 메서드와 유사하다는 것을 알고 있습니다. 따라서 전달된 콜백 함수를 수정하여 클래스 구성 요소에 있던 stale-while-refresh 캐싱을 생성할 수 있습니다. useEffect 후크를 제외하고는 모든 것이 동일하게 유지됩니다.

 const CACHE = {}; export default function Component({ page }) { const [users, setUsers] = useState([]); useEffect(() => { if (CACHE[page] !== undefined) { setUsers(CACHE[page]); } apiFetch(`https://reqres.in/api/users?page=${page}`).then(json => { CACHE[page] = json.data; setUsers(json.data); }); }, [page]); // ... create usersDOM from users return <div>{usersDOM}</div>; }

따라서 함수 구성 요소에서 작동하는 stale-while-refresh 캐싱이 있습니다.

두 번째 구성 요소에 대해서도 동일한 작업을 수행할 수 있습니다. 즉, 기능으로 변환하고 stale-while-refresh 캐싱을 구현합니다. 결과는 수업에서 했던 것과 동일할 것입니다.

그러나 그것은 클래스 구성 요소보다 낫지 않습니까? 따라서 사용자 지정 후크의 기능을 사용하여 여러 구성 요소에서 사용할 수 있는 모듈식 stale-while-refresh 논리를 만드는 방법을 살펴보겠습니다.

새로 고침하는 동안 사용자 지정 부실 후크

먼저 커스텀 훅으로 옮기고자 하는 로직의 범위를 좁혀봅시다. 이전 코드를 보면 useStateuseEffect 부분임을 알 수 있습니다. 보다 구체적으로 이것은 우리가 모듈화하려는 논리입니다.

 const [users, setUsers] = useState([]); useEffect(() => { if (CACHE[page] !== undefined) { setUsers(CACHE[page]); } apiFetch(`https://reqres.in/api/users?page=${page}`).then(json => { CACHE[page] = json.data; setUsers(json.data); }); }, [page]);

일반적으로 만들어야 하므로 URL을 동적으로 만들어야 합니다. 그래서 우리는 url 을 인수로 가질 필요가 있습니다. 여러 요청이 동일한 page 값을 가질 수 있으므로 캐싱 논리도 업데이트해야 합니다. 운 좋게도 page 가 끝점 URL에 포함되면 모든 고유한 요청에 대해 고유한 값이 생성됩니다. 따라서 전체 URL을 캐싱을 위한 키로 사용할 수 있습니다.

 const [data, setData] = useState([]); useEffect(() => { if (CACHE[url] !== undefined) { setData(CACHE[url]); } apiFetch(url).then(json => { CACHE[url] = json.data; setData(json.data); }); }, [url]);

그 정도입니다. 함수 안에 래핑한 후 사용자 정의 후크를 갖게 됩니다. 아래를 살펴보십시오.

 const CACHE = {}; export default function useStaleRefresh(url, defaultValue = []) { const [data, setData] = useState(defaultValue); useEffect(() => { // cacheID is how a cache is identified against a unique request const cacheID = url; // look in cache and set response if present if (CACHE[cacheID] !== undefined) { setData(CACHE[cacheID]); } // fetch new data apiFetch(url).then(newData => { CACHE[cacheID] = newData.data; setData(newData.data); }); }, [url]); return data; }

defaultValue 라는 또 다른 인수를 추가했음을 주목하십시오. 이 후크를 여러 구성 요소에서 사용하는 경우 API 호출의 기본값이 다를 수 있습니다. 그래서 맞춤형으로 만들었습니다.

newData 객체의 data 키에 대해서도 동일한 작업을 수행할 수 있습니다. 사용자 정의 후크가 다양한 데이터를 반환하는 경우 newData.data 가 아닌 newData 만 반환하고 구성 요소 측에서 해당 순회를 처리할 수 있습니다.

이제 stale-while-refresh 캐싱의 무거운 작업을 수행하는 사용자 지정 후크가 있으므로 구성 요소에 연결하는 방법이 있습니다. 우리가 줄일 수 있었던 엄청난 양의 코드에 주목하십시오. 우리의 전체 구성 요소는 이제 단 세 개의 명령문입니다. 큰 승리입니다.

 import useStaleRefresh from "./useStaleRefresh"; export default function Component({ page }) { const users = useStaleRefresh(`https://reqres.in/api/users?page=${page}`, []); const usersDOM = users.map(user => ( <p key={user.id}> <img src={user.avatar} alt={user.first_name} style={{ height: 24, width: 24 }} /> {user.first_name} {user.last_name} </p> )); return <div>{usersDOM}</div>; }

두 번째 구성 요소에 대해서도 동일한 작업을 수행할 수 있습니다. 다음과 같이 표시됩니다.

 export default function Component2({ page }) { const cats = useStaleRefresh(`https://reqres.in/api/cats?page=${page}`, []); // ... create catsDOM from cats return <div>{catsDOM}</div>; }

이 후크를 사용하면 얼마나 많은 상용구 코드를 절약할 수 있는지 쉽게 알 수 있습니다. 코드도 더 좋아 보입니다. 전체 앱이 작동하는 모습을 보려면 이 CodeSandbox로 이동하십시오.

useStaleRefresh 에 로딩 표시기 추가

이제 기본 사항을 파악했으므로 사용자 지정 후크에 더 많은 기능을 추가할 수 있습니다. 예를 들어 고유한 요청이 전송될 때마다 true인 후크에 isLoading 값을 추가할 수 있으며 그 동안에는 표시할 캐시가 없습니다.

isLoading 에 대한 별도의 상태를 갖고 후크의 상태에 따라 설정하여 이를 수행합니다. 즉, 캐시된 웹 콘텐츠를 사용할 수 없으면 true 로 설정하고 그렇지 않으면 false 로 설정합니다.

다음은 업데이트된 후크입니다.

 export default function useStaleRefresh(url, defaultValue = []) { const [data, setData] = useState(defaultValue); const [isLoading, setLoading] = useState(true); useEffect(() => { // cacheID is how a cache is identified against a unique request const cacheID = url; // look in cache and set response if present if (CACHE[cacheID] !== undefined) { setData(CACHE[cacheID]); setLoading(false); } else { // else make sure loading set to true setLoading(true); } // fetch new data apiFetch(url).then(newData => { CACHE[cacheID] = newData.data; setData(newData.data); setLoading(false); }); }, [url]); return [data, isLoading]; }

이제 구성 요소에서 새로운 isLoading 값을 사용할 수 있습니다.

 export default function Component({ page }) { const [users, isLoading] = useStaleRefresh( `https://reqres.in/api/users?page=${page}`, [] ); if (isLoading) { return <div>Loading</div>; } // ... create usersDOM from users return <div>{usersDOM}</div>; }

완료되면 고유한 요청이 처음 전송되고 캐시가 없을 때 "Loading" 텍스트가 표시됩니다.

로딩 표시기가 구현된 구성요소를 보여주는 애니메이션입니다.

useStaleRefresh 지원 모든 async 함수 만들기

GET 네트워크 요청이 아닌 모든 async 기능을 지원하도록 하여 사용자 정의 후크를 훨씬 더 강력하게 만들 수 있습니다. 기본 아이디어는 그대로 유지됩니다.

  1. 후크에서 일정 시간 후에 값을 반환하는 비동기 함수를 호출합니다.
  2. 비동기 함수에 대한 각각의 고유한 호출은 적절하게 캐시됩니다.

function.namearguments 의 간단한 연결은 우리의 사용 사례에 대한 캐시 키로 작동합니다. 이를 사용하면 다음과 같이 후크가 표시됩니다.

 import { useState, useEffect, useRef } from "react"; import isEqual from "lodash/isEqual"; const CACHE = {}; export default function useStaleRefresh(fn, args, defaultValue = []) { const prevArgs = useRef(null); const [data, setData] = useState(defaultValue); const [isLoading, setLoading] = useState(true); useEffect(() => { // args is an object so deep compare to rule out false changes if (isEqual(args, prevArgs.current)) { return; } // cacheID is how a cache is identified against a unique request const cacheID = hashArgs(fn.name, ...args); // look in cache and set response if present if (CACHE[cacheID] !== undefined) { setData(CACHE[cacheID]); setLoading(false); } else { // else make sure loading set to true setLoading(true); } // fetch new data fn(...args).then(newData => { CACHE[cacheID] = newData; setData(newData); setLoading(false); }); }, [args, fn]); useEffect(() => { prevArgs.current = args; }); return [data, isLoading]; } function hashArgs(...args) { return args.reduce((acc, arg) => stringify(arg) + ":" + acc, ""); } function stringify(val) { return typeof val === "object" ? JSON.stringify(val) : String(val); }

보시다시피, 우리는 함수 호출을 고유하게 식별하여 캐시하기 위해 함수 이름과 문자열화된 인수의 조합을 사용하고 있습니다. 이것은 우리의 간단한 앱에서 작동하지만 이 알고리즘은 충돌과 느린 비교가 발생하기 쉽습니다. (직렬화할 수 없는 인수를 사용하면 전혀 작동하지 않습니다.) 따라서 실제 앱의 경우 적절한 해싱 알고리즘이 더 적합합니다.

여기서 주목해야 할 또 다른 사항은 useRef 의 사용입니다. useRef 는 둘러싸는 구성 요소의 전체 수명 주기 동안 데이터를 유지하는 데 사용됩니다. args 는 JavaScript의 객체인 배열이기 때문에 후크를 사용하여 구성 요소를 다시 렌더링할 때마다 args 참조 포인터가 변경됩니다. 그러나 args 는 첫 번째 useEffect 의 종속성 목록의 일부입니다. 따라서 args 를 변경하면 아무 것도 변경되지 않은 경우에도 useEffect 가 실행될 수 있습니다. 이에 대응하기 위해 isEqual을 사용하여 이전 인수와 현재 args 를 심층 비교하고 args 가 실제로 변경된 경우에만 useEffect 콜백이 실행되도록 합니다.

이제 이 새로운 useStaleRefresh 후크를 다음과 같이 사용할 수 있습니다. 여기에서 defaultValue 의 변경 사항을 확인하십시오. 범용 후크이므로 응답 개체의 data 키를 반환하기 위해 후크에 의존하지 않습니다.

 export default function Component({ page }) { const [users, isLoading] = useStaleRefresh( apiFetch, [`https://reqres.in/api/users?page=${page}`], { data: [] } ); if (isLoading) { return <div>Loading</div>; } const usersDOM = users.data.map(user => ( <p key={user.id}> <img src={user.avatar} alt={user.first_name} style={{ height: 24, width: 24 }} /> {user.first_name} {user.last_name} </p> )); return <div>{usersDOM}</div>; }

이 CodeSandbox에서 전체 코드를 찾을 수 있습니다.

사용자를 기다리게 하지 마십시오: Stale-while-refresh 및 React Hooks로 캐시 콘텐츠를 효과적으로 사용

이 기사에서 만든 useStaleRefresh 후크는 React Hooks로 가능한 것을 보여주는 개념 증명입니다. 코드를 사용하여 응용 프로그램에 맞출 수 있는지 확인하십시오.

또는 swr 또는 react-query와 같이 잘 관리된 인기 있는 오픈 소스 라이브러리를 통해 새로고침 중 stale-while-refresh를 활용할 수도 있습니다. 둘 다 강력한 라이브러리이며 API 요청에 도움이 되는 다양한 기능을 지원합니다.

React Hooks는 게임 체인저입니다. 구성 요소 논리를 우아하게 공유할 수 있습니다. 이전에는 구성 요소 상태, 수명 주기 메서드 및 렌더링이 모두 클래스 구성 요소라는 하나의 엔터티로 패키지되었기 때문에 불가능했습니다. 이제 우리는 그들 모두에 대해 다른 모듈을 가질 수 있습니다. 이것은 구성 가능성과 더 나은 코드 작성에 좋습니다. 나는 내가 작성하는 모든 새로운 React 코드에 대해 함수 구성 요소와 후크를 사용하고 있으며 모든 React 개발자에게 이것을 적극 권장합니다.

관련: Redux Toolkit 및 RTK 쿼리를 사용하여 React 앱 만들기