组件内部的函数未接收到最新版本的Redux状态以退出轮询

问题描述 投票:0回答:1

我有一个问题,我试图通过在if条件中使用Redux状态来停止某些轮询的执行。我曾经浏览过SO和博客的帖子,但是不幸的是,没有一个能解决我的问题。我检查了我是否正确使用了mapStateToProps,不可变地更新了状态,并且对异步操作使用了Redux-Thunk。我看过的一些帖子是:

[我在这篇文章中为轮询方法提供了有益的帮助:Incorporating async actions, promise.then() and recursive setTimeout whilst avoiding "deferred antipattern",但我想将redux-state用作真实的单一来源,但在我的用例中可能无法实现。

由于我有大量的代码,因此我为实现对实际问题的可读性,已对代码进行了精简,以仅包括相关方面。我很高兴将其全部发布,但希望使问题尽可能简洁。

Loader.js
import { connect } from 'react-redux';
import { delay } from '../../shared/utility'
import * as actions from '../../store/actions/index';

const Loader = (props) => {
  const pollDatabase = (jobId, pollFunction) => { 
    return delay(5000)
      .then(pollFunction(jobId))  
        .catch(err => console.log("Failed in pollDatabase function. Error: ", err))
        };

  const pollUntilComplete = (jobId, pollFunction) => { 
    return pollDatabase(jobId, pollFunction)
        .then(res => {
            console.log(props.loadJobCompletionStatus) // <- always null
            if (!props.loadJobCompletionStatus) { <-- This is always null which is the initial state in reducer
                return pollUntilComplete(jobId, pollFunction);
            }
        })
        .catch(err=>console.log("Failed in pollUntilComplete. Error: ", err));
    };


  const uploadHandler = () => {
  ...
    const transferPromise = apiCall1() // Names changed to reduce code
      .then(res=> {
        return axios.post(api2url, res.data.id);
            })
      .then(postResponse=> {
        return axios.put(api3url, file)
        .then(()=>{ 
          return instance.post(api3url, postResponse.data) 
        })
      })

  transferDataPromise.then((res) => {
    return pollUntilComplete(res.data.job_id, 
      props.checkLoadTaskStatus)
        })
        .then(res => console.log("Task complete: ", res))
        .catch(err => console.log("An error occurred: ", err))
  }

return ( ...); // 

const mapStateToProps = state => {
    return {
        datasets: state.datasets,
        loadJobCompletionStatus: state.loadJobCompletionStatus,
        loadJobErrorStatus: state.loadJobErrorStatus,
        loadJobIsPolling: state.loadJobPollingFirestore
    }
}

const mapDispatchToProps = dispatch => {
  return {
    checkLoadTaskStatus: (jobId) => 
         dispatch(actions.loadTaskStatusInit(jobId))
    };
};


export default connect(mapStateToProps, mapDispatchToProps)(DataLoader);


delay.js
export const delay = (millis) => {
    return new Promise((resolve) => setTimeout(resolve, millis));
}

actions.js
...
export const loadTaskStatusInit = (jobId) => {
  return dispatch => {
      dispatch(loadTaskStatusStart()); // 
      const docRef = firestore.collection('coll').doc(jobId) 
        return docRef.get() 
        .then(jobData=>{
            const completionStatus = jobData.data().complete;
            const errorStatus = jobData.data().error;
            dispatch(loadTaskStatusSuccess(completionStatus, errorStatus))
        },
        error => {
            dispatch(loadTaskStatusFail(error));
        })
    };
}


似乎当我在控制台日志中时,props.loadJobCompletionStatus的值始终为null,这是我的reducer中的初始状态。使用Redux-dev工具,我看到状态确实在更新,并且所有动作都按我的预期进行。

我最初将props.loadJobCompletionStatus放置为pollDatabase的参数,并认为我可能已经创建了一个闭包,因此我删除了函数定义中的参数,以便该函数从“较高”级别获取结果范围,希望它能获取最新的Redux状态。我不确定为什么我会留下过时的状态。这导致我的if语句始终执行,因此我对数据库进行了无限轮询。

有人可以指出是什么原因造成的吗?

谢谢

javascript reactjs redux react-redux
1个回答
0
投票

我很确定这是因为您正在函数组件中定义一个闭包,因此闭包正在捕获对现有道具的引用在定义闭包时]。请参阅Dan Abramov's extensive post "The Complete Guide to useEffect"以更好地了解闭包和函数组件之间的关系。

作为替代方案,您可以将轮询逻辑移出组件,并在一个thunk中执行它(可以访问useEffect),或者使用getState()钩子具有一个可变的值,该值可以随时间访问(并可能在每次重新渲染后使用useRef()将最新的props值存储在该引用中)。可能存在现有的挂钩,它们也可以执行与useEffect()方法类似的操作。

© www.soinside.com 2019 - 2024. All rights reserved.