programing

react를 사용하여 x초마다 API 폴링

instargram 2023. 3. 13. 20:07
반응형

react를 사용하여 x초마다 API 폴링

1, 2초마다 화면에서 데이터 업데이트 정보를 모니터링해야 합니다.이 구현을 사용하는 방법은 다음과 같습니다.

    componentDidMount() {
        this.timer = setInterval(()=> this.getItems(), 1000);
      }
    
      componentWillUnmount() {
        this.timer = null;
      }
    
      getItems() {
        fetch(this.getEndpoint('api url endpoint'))
            .then(result => result.json())
            .then(result => this.setState({ items: result }));
      }

이것이 올바른 접근법입니까?

API만 있고 소켓을 사용하도록 변경할 수 있는 권한이 없기 때문에 폴링하는 방법밖에 없습니다.

여론조사에 따르면 당신은 적절한 접근을 하고 있습니다.하지만 위의 코드에는 한 가지 함정이 있습니다.

componentDidMount() {
  this.timer = setInterval(()=> this.getItems(), 1000);
}

componentWillUnmount() {
  this.timer = null; // here...
}

getItems() {
  fetch(this.getEndpoint('api url endpoint'))
    .then(result => result.json())
    .then(result => this.setState({ items: result }));
}

여기서의 문제는 컴포넌트가 마운트 해제되면 저장된 인터벌에 대한 참조가 됩니다.this.timer로 설정되어 있다.null, 아직 정지되지 않았습니다.컴포넌트가 마운트 해제된 후에도 인터벌은 핸들러를 계속 호출하여setState더 이상 존재하지 않는 컴포넌트에 있습니다.

적절하게 대처하기 위해clearInterval(this.timer)그 후 세팅this.timer = null.

또,fetch콜이 비동기이기 때문에 같은 문제가 발생할 수 있습니다.취소할 수 있도록 하고 있으면 취소합니다.fetch불완전합니다.

이게 도움이 됐으면 좋겠어요.

React Polling을 검색했을 때는 오래된 질문이었지만 Hooks에 맞는 답변이 없었습니다.

// utils.js

import React, { useState, useEffect, useRef } from 'react';

export const useInterval = (callback, delay) => {

  const savedCallback = useRef();

  useEffect(() => {
    savedCallback.current = callback;
  }, [callback]);


  useEffect(() => {
    function tick() {
      savedCallback.current();
    }
    if (delay !== null) {
      const id = setInterval(tick, delay);
      return () => clearInterval(id);
    }
  }, [delay]);
}

출처 : https://overreacted.io/making-setinterval-declarative-with-react-hooks/

그런 다음 Import하여 사용할 수 있습니다.

// MyPage.js

import useInterval from '../utils';

const MyPage = () => {

  useInterval(() => {
    // put your interval code here.
  }, 1000 * 10);

  return <div>my page content</div>;
}

다음과 같은 조합을 사용할 수 있습니다.setTimeout그리고.clearTimeout.

setInterval는 이전 콜의 성공 여부에 관계없이 'x'초마다 API 콜을 기동합니다.이로 인해 브라우저 메모리가 잠식되어 시간이 지남에 따라 성능이 저하될 수 있습니다.게다가 서버가 다운되었을 경우,setInterval는, 다운 상태를 모르는 채로 서버를 계속 폭격합니다.

반면에.

다음을 사용하여 재귀할 수 있습니다.setTimeout이전 API 호출이 성공한 경우에만 후속 API 호출을 실행합니다.이전의 콜이 실패했을 경우는, 타임 아웃을 클리어 해, 그 이후의 콜을 기동하지 말아 주세요.필요에 따라서, 실패했을 경우에 유저에게 경고합니다.이 프로세스를 재시작하려면 페이지를 새로 고치십시오.

다음은 코드 예시입니다.

let apiTimeout = setTimeout(fetchAPIData, 1000);

function fetchAPIData(){
    fetch('API_END_POINT')
    .then(res => {
            if(res.statusCode == 200){
                // Process the response and update the view.
                // Recreate a setTimeout API call which will be fired after 1 second.
                apiTimeout = setTimeout(fetchAPIData, 1000);
            }else{
                clearTimeout(apiTimeout);
                // Failure case. If required, alert the user.
            }
    })
    .fail(function(){
         clearTimeout(apiTimeout);
         // Failure case. If required, alert the user.
    });
}

@AmitJS94, 기사에서 GavKilbride가 언급한 메서드에 추가되는 인터벌을 중지하는 방법에 대한 자세한 섹션이 있습니다.

작성자는 지연 변수의 상태를 추가하고 간격을 일시 정지할 때 해당 지연에 대해 "null"을 전달하도록 지시했습니다.

const [delay, setDelay] = useState(1000);
const [isRunning, setIsRunning] = useState(true);
  useInterval(() => {
    setCount(count + 1);
  }, isRunning ? delay : null);

    useEffect(() => {
    function tick() {
      savedCallback.current();
    }

    if (delay !== null) {
      let id = setInterval(tick, delay);
      return () => clearInterval(id);
    }
  }, [delay]);

기사를 꼭 읽어보고 자세한 내용을 더 잘 이해하세요.완벽하고 잘 쓰여져 있어요!

Vasanth가 언급했듯이, 나는 다음을 선호한다.

  • set Timeout을 사용하여 마지막 요청 종료 후 다음 요청 시작까지의 시간을 측정합니다.
  • 지연 후가 아니라 즉시 첫 번째 요청을 한다
  • @KyleMit https://stackoverflow.com/a/64654157/343900의 답변에서 영감을 얻어
import { useEffect, useRef } from 'react';

export const useInterval = (
  callback: Function,
  fnCondition: Function,
  delay: number,
) => {
  const savedCallback = useRef<Function>();
  useEffect(() => {
    savedCallback.current = callback;
  }, [callback]);
  useEffect(() => {
    let id: NodeJS.Timeout;
    const tick = async () => {
      try {
        const response =
          typeof savedCallback.current === 'function' &&
          (await savedCallback.current());
        if (fnCondition(response)) {
          id = setTimeout(tick, delay);
        } else {
          clearTimeout(id);
        }
      } catch (e) {
        console.error(e);
      }
    };
    tick();
    return () => id && clearTimeout(id);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [delay]);
};

WORKS: fnCondition inside를 사용합니다.이것은 마지막 요구로부터의 응답에 근거한 조건일 수 있습니다.

//axios-hooks
const {
    data,
    isLoadingData,
    getData,
} = api.useGetData();

const fnCondition = (result: any) => {
    const randomContidion = Math.random();
    //return true to continue
    return randomContidion < 0.9;
  };
useInterval(() => getData(), fnCondition, 1000);

작동하지 않음: 지연을 null로 전달하여 useInterval중지하는 것은 다음 코드에서는 작동하지 않습니다.https://www.aaron-powell.com/posts/2019-09-23-recursive-settimeout-with-react-hooks/

(동작하는 것 같은 느낌이 들 수도 있지만, 몇 번을 시작/정지하면 고장납니다.

  const [isRunning, setIsRunning] = useState(true);
  const handleOnclick = () => {
    setIsRunning(!isRunning);
  };

  useInterval(() => getData(), isRunning ? 1000 : null);
  <button onClick={handleOnclick}>{isRunning ? 'Stop' : 'Start'}</button>

요약: fnCondition을 전달하여 useInterval을 중지할 수 있지만, 지연을 전달하여 useInterval을 중지할 수 없습니다.

다음은 심플하고 완전한 솔루션입니다.

  • X초마다 폴링

  • 로직이 실행될 때마다 타임아웃을 늘려 서버에 과부하가 걸리지 않도록 하는 옵션이 있습니다.

  • 최종 사용자가 컴포넌트를 종료할 때 타임아웃을 클리어합니다.

     //mount data
     componentDidMount() {
         //run this function to get your data for the first time
         this.getYourData();
         //use the setTimeout to poll continuously, but each time increase the timer
         this.timer = setTimeout(this.timeoutIncreaser, this.timeoutCounter);
     }
    
     //unmounting process
     componentWillUnmount() {
         this.timer = null; //clear variable
         this.timeoutIncreaser = null; //clear function that resets timer
     }
    
     //increase by timeout by certain amount each time this is ran, and call fetchData() to reload screen
     timeoutIncreaser = () => {
         this.timeoutCounter += 1000 * 2; //increase timeout by 2 seconds every time
         this.getYourData(); //this can be any function that you want ran every x seconds
         setTimeout(this.timeoutIncreaser, this.timeoutCounter);
     }
    

함수 컴포넌트에서 후크를 사용하는 간단한 예를 다음에 나타냅니다.이 예에서는 설정된 간격으로 데이터가 갱신됩니다.

import React from 'react';

import { useEffect, useState } from 'react';

export default function App() {
  let [jokes, setJokes] = useState('Initial');

  async function fetchJokes() {
    let a = await fetch('https://api.chucknorris.io/jokes/random');
    let b = await a.json();
    setJokes(b.value);
  }

// Below function works like compomentWillUnmount and hence it clears the timeout
  useEffect(() => {
    let id = setTimeout(fetchJokes, 2000);
    return () => clearTimeout(id);
  });

  return <div>{jokes}</div>;
}

또는 API 호출에 액시스를 사용할 수도 있습니다.

function App() {
  const [state, setState] = useState("Loading.....");

  function fetchData() {
    axios.get(`https://api.chucknorris.io/jokes/random`).then((response) => {
      setState(response.data.value);
    });
  }

  useEffect(() => {
    console.log("Hi there!");
    let timerId = setTimeout(fetchData, 2000);
     return ()=> clearInterval(timerId); 
  });

  return (
    <>
      This component
      <h3>{state}</h3>
    </>
  );
}

언급URL : https://stackoverflow.com/questions/46140764/polling-api-every-x-seconds-with-react

반응형