我有一个问题,我试图通过在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语句始终执行,因此我对数据库进行了无限轮询。
有人可以指出是什么原因造成的吗?
谢谢
我很确定这是因为您正在函数组件中定义一个闭包,因此闭包正在捕获对现有道具的引用在定义闭包时]。请参阅Dan Abramov's extensive post "The Complete Guide to useEffect
"以更好地了解闭包和函数组件之间的关系。
作为替代方案,您可以将轮询逻辑移出组件,并在一个thunk中执行它(可以访问useEffect
),或者使用getState()
钩子具有一个可变的值,该值可以随时间访问(并可能在每次重新渲染后使用useRef()
将最新的props值存储在该引用中)。可能存在现有的挂钩,它们也可以执行与useEffect()
方法类似的操作。