首页 > 解决方案 > 如果已经有一个 API 调用正在进行,如何确保我不会再次调用我的 API(通过单击按钮)?

问题描述

我制作了这段代码,我对此有疑问。

在阅读问题之前,请参阅下面的片段。

它通过调用 API 来模仿博客文章的喜欢/不喜欢行为,我认为它按预期工作,但我不知道具体原因。我认为这很有趣。

这是我的思路:

问题

事实是,似乎不可能足够快地单击并同时触发多个 API 调用(同时运行)。这就是我想要实现的目标。

但我想知道的是:这是速度问题吗?React 更新状态的速度是否如此之快,以至于在重新创建之前无法再次单击按钮(现在为 true)?toggleFunctionprops.likeInProgress还是代码执行的顺序完全阻止了这种情况的发生,与速度无关?

更多解释:

当您真正快速单击 2 次时,您看到该日志的事实There is a call in progress意味着 React 已经重新渲染了所有内容,现在该toggleLike函数显示props.likeInProgresstrue. 如果这是速度问题,并且您单击得非常快,那么您将能够toggleLike从第一次单击时执行相同的操作,这props.likeInProgressfalse导致您再次调用 API。

function App() {
  
  //console.log('Rendering App...');

  // STATE TO MONITOR THE LIKE API CALL IN PROGRESS
  const [likeInProgress,setLikeInProgress] = React.useState(false);
  
  // STATE TO KNOW WHETHER A POST HAS BEEN LIKED OR NOT
  const [hasLiked,setHasLiked] = React.useState(false);
  
  // REF TO KEEP TRACK OF THE LIKE COUNT
  const likeCount_ref = React.useRef(15);
  
  // THIS IS TO MOCK A LIKE API CALL (1000 ms DELAY ASYNC)
  function mockLikeAPI(action) {
    return new Promise((resolve,reject) => {
      setTimeout(()=>{
        resolve('Done');
      },1000);
    });
  }

  // THIS IS THE CALL TO THE LIKE API
  function callMockLikeAPI(action) {
    setLikeInProgress(true);                              // SET STATE likeInProgress TO 'TRUE'
    mockLikeAPI(action).then(()=> {                       // CALL THE API
      likeCount_ref.current = action === 'LIKE' ?         // UPDATE COUNTER ACCORDINGLY
        likeCount_ref.current + 1 
      : likeCount_ref.current - 1;
      action === 'LIKE' ? setHasLiked(true) : setHasLiked(false);   // UPDATE hasLiked STATE
      setLikeInProgress(false);
    });
  }

  return(
    <BlogPost
      likeCount={likeCount_ref.current}
      likeInProgress={likeInProgress}
      hasLiked={hasLiked}
      callMockLikeAPI={callMockLikeAPI}
    />
  );
}

function BlogPost(props) {

  //console.log('Rendering BlogPost...');
  
  // FUNCTION TO TOGGLE THE LIKE OF THE POST
  function toggleLike() {
    if (props.likeInProgress) {                       // IF THERE'S A LIKE IN PROGRESS
      console.log('There is a call in progress...');  // LOG
      return;                                         // DO NOTHING. RETURN;
    }
    else {                                        // ELSE
      console.log('I will call the API now...');  // LOG
      props.hasLiked === false ?                  // CALL THE API WITH THE PROPER ACTION
        props.callMockLikeAPI('LIKE') 
      : props.callMockLikeAPI('DISLIKE');
    }
  }
  
  return(
    <React.Fragment>
      <div>I am a Blog Post</div>
      <div>Like Count: {props.likeCount}</div>
      <button onClick={toggleLike}>{props.hasLiked === false ? 'Like' : 'Dislike'}</button>
    </React.Fragment>
  );
}

ReactDOM.render(<App/>, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"/>

标签: javascriptreactjsasynchronousreact-hooks

解决方案


用户单击按钮后隐藏按钮,将其替换为 gif 加载器,然后在调用完成后再次显示。您将需要在网络调用上实现回调。这将保证用户不能call the api twice因为带有 onclick 事件的按钮现在被隐藏。

btnWithNetCall(parameter1){
   // hide your button via attribute or css
   attemptAPIcall(param1, function(response){
       //show your button again after checking what happened with response
   })
} 


attemptAPIcall(param1, callback){
  // fire callback with pertinent data after doing networky thing
  callback(someSortOfReponseData)
}

推荐阅读