场景
需求:点击支付的时候,会出现支付的二维码,这时候我不知道用户什么时候支付完成了,这时候就需要轮询请求接口,根据接口的返回值判断是否支付成功了,如果用户取消支付,那就不在轮询请求接口了。
问题: 当我用useState的值来控制是否开始还是停止轮询接口的时候,我发现根本就无法去获取到最新的state。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 
 | function App() {const [visible, setVisible] = useState(false)
 useEffect(() => {
 if(visible){
 payHandle()
 }
 }, [visible])
 
 
 const payHandle = async () => {
 console.log(visible);
 if (!visible) {
 return;
 }
 const res = await axios('http://pic.616pic.com/ys_bnew_img/00/01/69/BnYq61qLwM.jpg')
 console.log('请求中...')
 
 if (!res.isSuccess) {
 
 await new Promise((resolve) => setTimeout(resolve, 3000));
 payHandle()
 return;
 }
 location.href = '/'
 }
 
 return (
 <div>
 {visible && <div>支付中。。。</div>}
 <button onClick={() => setVisible(true)}>支付</button>
 <button onClick={() => setVisible(false)}>取消支付</button>
 </div>
 )
 }
 
 | 
打印的结果如下:
![0.png]()
一开始点击支付,然后会一直请求这是没有错的,但是点击取消支付就会发现请求还是在一直继续,payHandle函数一直在轮询。
原因
react 官方文档有提到,大概的意思是组件内部的函数只会拿到定义它的那次渲染的props和state。因为这个是当支付的时候,payHandle函数就会一直调用执行,即使取消支付,但它也并没有重新定义改函数,运行的还是之前的payHandle函数,所以是无法拿到useState的最新值。
解决方案
采用useRef来解决问题。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 
 | function App() {const [visible, setVisible] = useState(false)
 const isQuerying = useRef(false)
 
 useEffect(() => {
 isQuerying.current = visible;
 if(visible){
 payHandle()
 }
 }, [visible])
 
 
 const payHandle = async () => {
 console.log(isQuerying.current);
 if (!isQuerying.current) {
 return;
 }
 const res: any = await axios('http://pic.616pic.com/ys_bnew_img/00/01/69/BnYq61qLwM.jpg')
 console.log('请求中...')
 
 if (!res.isSuccess) {
 
 await new Promise((resolve) => setTimeout(resolve, 3000));
 payHandle()
 return;
 }
 location.href = '/'
 }
 
 return (
 <div>
 {visible && <div>支付中。。。</div>}
 <button onClick={() => setVisible(true)}>支付</button>
 <button onClick={() => setVisible(false)}>取消支付</button>
 </div>
 )
 }
 
 | 
运行结果如下:
![1.png]()
可以看到点击支付,之后再点击取消支付就可以停止发送请求了。