reactjs 如何在REACT中每分钟为 Jmeter 板调用一次API

h79rfbju  于 2023-03-17  发布在  React
关注(0)|答案(7)|浏览(137)

我在React中做了一个 Jmeter 板。它没有主动更新,没有按钮、字段或下拉菜单。它将部署在一个壁挂式电视上供查看。所有面板(总共9个)都通过API调用更新。初始调用(见下文)工作正常,所有JSON数据都被获取, Jmeter 板也得到了初始更新。
底线问题:我需要在初始调用后每隔30秒到1分钟调用一次API来检查更新。
我曾尝试在componentDidMount()中使用“setInterval”,正如这里回答其他人问题的人所建议的那样,但我得到了一个错误“await is a reserved word”。我读过关于forceUpdate()的文章,考虑到facebook/react页面对它的描述,它似乎符合我的用例逻辑。但是,我也读过这里,以远离它...
下面的代码是我正在使用的代码的精简版。为了简洁起见,我删除了许多组件和导入。任何帮助都将不胜感激。

import React, { Component } from 'react';

import Panelone from './Components/Panelone';
import Paneltwo from './Components/Paneltwo';

class App extends Component {

  state = {
      panelone: [],
      paneltwo: []
    }

 async componentDidMount() {
      try {
        const res = await fetch('https://api.apijson.com/...');
        const blocks = await res.json();
        const dataPanelone = blocks.panelone;
        const dataPaneltwo = blocks.paneltwo;

        this.setState({
          panelone: dataPanelone,
          paneltwo: dataPaneltwo,
        })
      } catch(e) {
        console.log(e);
      }

  render () {
    return (
      <div className="App">
        <div className="wrapper">
          <Panelone panelone={this.state} />
          <Paneltwo paneltwo={this.state} />
        </div>
      </div>
    );
  }
}

export default App;
x8goxv8g

x8goxv8g1#

将数据获取逻辑移到一个单独的函数中,并使用componentDidMount方法中的setInterval调用该函数,如下所示。

componentDidMount() {
    this.loadData()
    setInterval(this.loadData, 30000);
  }

  async loadData() {
     try {
        const res = await fetch('https://api.apijson.com/...');
        const blocks = await res.json();
        const dataPanelone = blocks.panelone;
        const dataPaneltwo = blocks.paneltwo;

        this.setState({
           panelone: dataPanelone,
           paneltwo: dataPaneltwo,
        })
    } catch (e) {
        console.log(e);
    }
  }

下面是一个工作示例
https://codesandbox.io/s/qvzj6005w

xmd2e60i

xmd2e60i2#

为了使用await,直接封装它的函数必须是async。根据您的说法,如果您想在componentDidMount中使用setInterval,则在内部函数中添加async将解决此问题。以下是代码,

async componentDidMount() {
          try {
            setInterval(async () => {
              const res = await fetch('https://api.apijson.com/...');
              const blocks = await res.json();
              const dataPanelone = blocks.panelone;
              const dataPaneltwo = blocks.paneltwo;

              this.setState({
                panelone: dataPanelone,
                paneltwo: dataPaneltwo,
              })
            }, 30000);
          } catch(e) {
            console.log(e);
          }
    }

此外,您应该考虑使用react-timer-mixin,而不是全局使用setInterval。https://facebook.github.io/react-native/docs/timers.html#timermixin

wbrvyc0a

wbrvyc0a3#

对于那些寻找功能组件的人,可以通过创建setInterval并在useEffect钩子中调用它来每n次更新状态,最后在clean up函数中调用clearInterval方法

import React, { useEffect, useState } from "react";

import Panelone from "./Components/Panelone";
import Paneltwo from "./Components/Paneltwo";

function App() {
  const [state, setState] = useState({
    panelone: [],
    paneltwo: [],
  });

  const getData = async () => {
    try {
      const res = await fetch("https://api.apijson.com/...");
      const blocks = await res.json();
      const dataPanelone = blocks.panelone;
      const dataPaneltwo = blocks.paneltwo;

      setState({
        panelone: dataPanelone,
        paneltwo: dataPaneltwo,
      });
    } catch (e) {
      console.log(e);
    }
  };

  useEffect(() => {
    const intervalCall = setInterval(() => {
      getData();
    }, 30000);
    return () => {
      // clean up
      clearInterval(intervalCall);
    };
  }, []);
  return (
    <div className="App">
      <div className="wrapper">
        <Panelone panelone={state} />
        <Paneltwo paneltwo={state} />
      </div>
    </div>
  );
}

export default App;
ao218c7q

ao218c7q4#

我想我可以稍微修改一下方法,通过函数块中的setTimeout调用来使用递归。工作原理是一样的......也许稍微干净一点,让函数从内部调用自己,而不是在代码的其他地方这样做。
This article更深入地解释了其中的原因......但是我已经在工作中的几个 Jmeter 板上使用了这种方法--完成了工作!
看起来像这样:

class MyComponent extends React.Component
//create the instance for your interval
intervalID;
constructor(props) {
    super(props);
    this.state = {
        data: [],
        loading: false,
        loadingMap: false,

        //call in didMount...
        componentDidMount() {
          this.getTheData()
        }

 getTheData() {
 //set a loading state - good practice so you add a loading spinner or something
   this.setState({loading: true}), () => {
 //call an anonymous function and do your data fetching, then your setState for the data, and set loading back to false
   this.setState({
       data: fetchedData,
       loading: false
       )}     }
 //Then call the function again with setTimeout, it will keep running at the specified //interval...5 minutes in this case
            this.intervalID = setTimeout(
              this.getTheData.bind(this),
              300000
            );

          }
        }
        //Important! Be sure to clear the interval when the component unmounts! Your app might crash without this, or create memory leaks!
        componentWillUnmount() {
          clearTimeout(this.intervalID);
        }

对不起,如果格式有点过了。还没有尝试过这个钩子,但我想你会有一个类似的实现在useEffect调用?有人这样做了吗?

hjqgdpho

hjqgdpho5#

我已经看到了很多关于这个的复杂性。没有必要在生命周期或者状态或者承诺中有它。在这里,服务API只是一个简单的axios api调用
这是我使用context API时的完整实现(省略了一些私有代码),我只关心api中的状态响应,因为我知道我需要改变什么,但api可以是任何你需要的数据。

export class MyContextApiComponent ..... {
    private timeout: ReturnType<typeof setInterval> | undefined
    ...
    ...
    ...
    public statsPolling = (S_UUID: string) => {
        if (!this.timeout) {
            this.timeout = setInterval( () => { 
                this.statsPolling(S_UUID)
            }, 3000)
        }

        this.state.api.StatisticsService.statsPolling(S_UUID)
            .then(res => {
                if (res.hasDescStats) {
                    clearInterval(this.timeout)
                    
                    this.setState(prevState => ({
                        ...prevState,
                        ...
                        ...
                    }))
                }
            })
            .catch(e => console.warn('', e))
    }
   ...
   ...
}

/// in another file in service is the api call itself with axios just checking on the server reply status

export class Statistics implements IStatistics {
    public statsPolling: StatsPolling = async S_UUID => {
        return axios
            .get<{ hasDescStats: boolean }>(`/v2/api/polling?query=${S_UUID}`)
            .then(res => {
                if (res.status === 200) {
                    return { hasDescStats: true }
                } else {
                    return { hasDescStats: false }
                }
            })
    }
}
polhcujo

polhcujo6#

答案

您可以为componentDidMount代码创建一个函数。

import React, { Component } from 'react';

import Panelone from './Components/Panelone';
import Paneltwo from './Components/Paneltwo';

class App extends Component {

  state = {
      panelone: [],
      paneltwo: []
    }
code = async () => {
try {
        const res = await fetch('https://api.apijson.com/...');
        const blocks = await res.json();
        const dataPanelone = blocks.panelone;
        const dataPaneltwo = blocks.paneltwo;

        this.setState({
          panelone: dataPanelone,
          paneltwo: dataPaneltwo,
        })
      } catch(e) {
        console.log(e);
      }
}

 componentDidMount() {
    
      }

  render () {
    return (
      <div className="App">
        <div className="wrapper">
          <Panelone panelone={this.state} />
          <Paneltwo paneltwo={this.state} />
        </div>
      </div>
    );
  }
}

export default App;

然后创建一个组件DidUpdate

import React, { Component } from 'react';

import Panelone from './Components/Panelone';
import Paneltwo from './Components/Paneltwo';

class App extends Component {

  state = {
      panelone: [],
      paneltwo: []
    }
code = async () => {
try {
        const res = await fetch('https://api.apijson.com/...');
        const blocks = await res.json();
        const dataPanelone = blocks.panelone;
        const dataPaneltwo = blocks.paneltwo;

        this.setState({
          panelone: dataPanelone,
          paneltwo: dataPaneltwo,
        })
      } catch(e) {
        console.log(e);
      }
}

 componentDidMount() {
    this.code()    
 }
 componentDidUpdate(){
this.code()
}
  render () {
    return (
      <div className="App">
        <div className="wrapper">
          <Panelone panelone={this.state} />
          <Paneltwo paneltwo={this.state} />
        </div>
      </div>
    );
  }
}

export default App;
r8xiu3jd

r8xiu3jd7#

你还需要清除这个间隔,否则它会继续调用API。要清除间隔,你需要有一个引用,然后你可以在ComponentWillUnmount上清除它。你可以使用useRef钩子和你的类组件来达到这个目的。

class Test extends React.Component {
  constructor() {
    super(props);
    this.selectorRef = React.createRef(null);
   }

   componentDidMount() {
     this.selectorRef.current = setInterval(async () => {
     
       *// your function here*

     }, 30000);

   }

  componentWillUnmount() {
   clearInterval(this.selectorRef.current);
  }
}

功能组件方法

const intervalRef: { current: NodeJS.Timeout | null } = useRef(null);

useEffect(() => {
  intervalRef.current = setInterval(async () => {
  
  /* your function here. 
  ** call your api here
  */
  
  }, 30000);
  
  return () => {
      clearInterval(intervalRef.current as NodeJS.Timeout);
    }
}, []);

相关问题