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,
}));