React:从回调中设置功能组件的状态
React: SetState of Functional Component from Within a Callback
我的顶级功能组件 App
有一个 Promise 返回函数 req()
,它将被许多子组件调用。在内部,req()
更新 App
的状态以显示它已被调用(以及原因),然后调用另一个承诺返回函数。这里是 req()
:
//wrap all requests to track and display their progress
function req(func, args, waitCap, yayCap) {
//perform a callback on a given req, then update state
const withReq = (argId, callback) => {
let newReqs = state.reqList.map ( r => r); //copy the reqList
for (let reqIndex = 0; reqIndex < newReqs.length; reqIndex++) { //iterate through the list
if ((newReqs[reqIndex] && (newReqs[reqIndex].id === argId))) { //find a match
callback(newReqs[reqIndex]); //pass it to the callback
break;
}
}
setState( prevState => ({
...prevState,
reqList:newReqs,
}));
}
//kill a req and update state
const deleteReq = argId => {
let newReqs = state.reqList.filter( r => { //new reqList is the same list with no objects containing the argID
return r.id !== argId;
});
setState( prevState => ({
...prevState,
reqList:newReqs,
}));
}
//duplicate the req list
let newReqs = state.reqList.map( r => r );
const now = new Date(); //create a unique ID for this req for tracking
const reqId = [
now.getFullYear(),
String(now.getMonth()+1).padStart(2,"0"),
String(now.getDate()).padStart(2,"0"),
String(now.getHours()).padStart(2,"0"),
String(now.getMinutes()).padStart(2,"0"),
String(now.getSeconds()).padStart(2,"0"),
String(Math.floor(Math.random()*10000)).padStart(4,"0"),
].join("");
newReqs.push({ //add the new req to the new reqList
status:"waiting",
caption:waitCap,
id:reqId,
});
setState( prevState => ({ //render the changed list of Reqs
...prevState,
reqList:newReqs,
}));
return ServerCalls[func](args)
.then((res)=>{
withReq(reqId, foundReq =>{ //update the req to show success
foundReq.status="success";
foundReq.caption=yayCap;
});
setTimeout(() => {
deleteReq(reqId); //remove it from display after 3 seconds
}, 3000);
return res;
})
.catch((err)=>{
withReq(reqId, foundReq =>{ //update the req to show failure
foundReq.status="failure";
foundReq.caption=foundReq.caption+" Failed!";
});
setTimeout(() => {
deleteReq(reqId); //remove it from display after 3 seconds
}, 3000);
throw err;
})
}
这里的问题是 Promise.then()
和 Promise.catch()
中的回调函数对状态的初始值进行操作,而不是回调执行时它所具有的值,这是由于作用域的原因。这不是 class 组件的问题,只是功能组件的问题。
有没有办法让功能组件从回调中读取其执行时状态?还是需要解决方法?
这里有2个问题:
- 当你这样做时,你正在改变现有状态
withReq(reqId, foundReq => { //update the req to show success
foundReq.status = "success";
foundReq.caption = yayCap;
});
永远不要在 React 中改变状态 - 它会导致重新渲染问题。
.then
回调中的值已过时。通过在状态 setter 函数中向 callback
传递 当前(新更新的)状态 而不是旧状态来解决此问题:
const withReq = (argId, callback) => {
setState(prevState => ({
...prevState,
reqList: prevState.reqList.map(req => (
req.id === argId ? callback(req) : req
)),
}));
}
然后确保 callback
不会发生变异,而是创建并 returns 一个新对象,例如:
withReq(reqId, foundReq => ({
...foundReq,
status: "success",
caption: yayCap,
}));
我的顶级功能组件 App
有一个 Promise 返回函数 req()
,它将被许多子组件调用。在内部,req()
更新 App
的状态以显示它已被调用(以及原因),然后调用另一个承诺返回函数。这里是 req()
:
//wrap all requests to track and display their progress
function req(func, args, waitCap, yayCap) {
//perform a callback on a given req, then update state
const withReq = (argId, callback) => {
let newReqs = state.reqList.map ( r => r); //copy the reqList
for (let reqIndex = 0; reqIndex < newReqs.length; reqIndex++) { //iterate through the list
if ((newReqs[reqIndex] && (newReqs[reqIndex].id === argId))) { //find a match
callback(newReqs[reqIndex]); //pass it to the callback
break;
}
}
setState( prevState => ({
...prevState,
reqList:newReqs,
}));
}
//kill a req and update state
const deleteReq = argId => {
let newReqs = state.reqList.filter( r => { //new reqList is the same list with no objects containing the argID
return r.id !== argId;
});
setState( prevState => ({
...prevState,
reqList:newReqs,
}));
}
//duplicate the req list
let newReqs = state.reqList.map( r => r );
const now = new Date(); //create a unique ID for this req for tracking
const reqId = [
now.getFullYear(),
String(now.getMonth()+1).padStart(2,"0"),
String(now.getDate()).padStart(2,"0"),
String(now.getHours()).padStart(2,"0"),
String(now.getMinutes()).padStart(2,"0"),
String(now.getSeconds()).padStart(2,"0"),
String(Math.floor(Math.random()*10000)).padStart(4,"0"),
].join("");
newReqs.push({ //add the new req to the new reqList
status:"waiting",
caption:waitCap,
id:reqId,
});
setState( prevState => ({ //render the changed list of Reqs
...prevState,
reqList:newReqs,
}));
return ServerCalls[func](args)
.then((res)=>{
withReq(reqId, foundReq =>{ //update the req to show success
foundReq.status="success";
foundReq.caption=yayCap;
});
setTimeout(() => {
deleteReq(reqId); //remove it from display after 3 seconds
}, 3000);
return res;
})
.catch((err)=>{
withReq(reqId, foundReq =>{ //update the req to show failure
foundReq.status="failure";
foundReq.caption=foundReq.caption+" Failed!";
});
setTimeout(() => {
deleteReq(reqId); //remove it from display after 3 seconds
}, 3000);
throw err;
})
}
这里的问题是 Promise.then()
和 Promise.catch()
中的回调函数对状态的初始值进行操作,而不是回调执行时它所具有的值,这是由于作用域的原因。这不是 class 组件的问题,只是功能组件的问题。
有没有办法让功能组件从回调中读取其执行时状态?还是需要解决方法?
这里有2个问题:
- 当你这样做时,你正在改变现有状态
withReq(reqId, foundReq => { //update the req to show success
foundReq.status = "success";
foundReq.caption = yayCap;
});
永远不要在 React 中改变状态 - 它会导致重新渲染问题。
.then
回调中的值已过时。通过在状态 setter 函数中向callback
传递 当前(新更新的)状态 而不是旧状态来解决此问题:
const withReq = (argId, callback) => {
setState(prevState => ({
...prevState,
reqList: prevState.reqList.map(req => (
req.id === argId ? callback(req) : req
)),
}));
}
然后确保 callback
不会发生变异,而是创建并 returns 一个新对象,例如:
withReq(reqId, foundReq => ({
...foundReq,
status: "success",
caption: yayCap,
}));