【问题标题】:React: SetState of Functional Component from Within a CallbackReact:在回调中设置功能组件的状态
【发布时间】:2021-05-05 00:20:57
【问题描述】:

我的顶级功能组件App 有一个承诺返回函数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() 中的回调函数在状态的初始值上运行,而不是在回调执行时的值,由于范围。这不是类组件的问题,只是功能性的。

功能组件有没有办法从回调中读取其执行时状态?还是需要解决方法?

【问题讨论】:

    标签: reactjs callback react-hooks


    【解决方案1】:

    这里有两个问题:

    • 你正在改变现有的状态
    withReq(reqId, foundReq => { //update the req to show success
        foundReq.status = "success";
        foundReq.caption = yayCap;
    });
    

    永远不要在 React 中改变状态 - 这可能会导致重新渲染问题。

    • .then 回调中的值已过时。通过在状态设置器函数中将当前(新更新的)状态而不是旧状态传递给callback 来解决此问题:
    const withReq = (argId, callback) => {
        setState(prevState => ({
            ...prevState,
            reqList: prevState.reqList.map(req => (
                req.id === argId ? callback(req) : req
            )),
        }));
    }
    

    然后确保callback 不会发生变异,而是创建并返回一个新对象,例如:

    withReq(reqId, foundReq => ({
        ...foundReq,
        status: "success",
        caption: yayCap,
    }));
    

    【讨论】:

    • 完成所有列出的更改后,即可实现所需的行为。但是,在我将此标记为正确答案之前,您能否澄清一下您指出的状态突变?我相信在我发布的功能中,没有状态发生变化。传递给withReq 的回调将对新创建的newReqs 数组的元素执行其操作。这不能解决问题吗?
    • 数组是新的,但对象是一样的。您没有深度克隆数组,只是创建一个新容器,其中存在相同的对象。就像const obj = {}; arr1.push(obj); arr2.push(obj); obj.foo = 'bar'
    猜你喜欢
    • 2021-04-17
    • 1970-01-01
    • 2020-08-23
    • 2020-05-07
    • 2021-10-08
    • 2021-08-07
    • 1970-01-01
    • 1970-01-01
    • 2020-07-09
    相关资源
    最近更新 更多