反应-在Dependency更改时在useEffect中使用Dependencies而不触发效果本身

时间:2020-09-14 15:59:29

标签: reactjs

我有反应状态

const [list, setList] = useState([])

和修改列表时触发的反应效果,并对列表中的第一个元素进行一些工作:

useEffect( () => {
  if(list.length <= 0) return

  //Do something with the first element of the list

  //Remove first element of the list

}, [list])

通过这种方式,效果会自己触发n次,其中n是列表中元素的编号。

然后我的组件中还有另一个方法,可以使用setList()方法在列表中插入元素,我们称之为

insertElemInList = () => {
  //insert one or more elements in list
}

在调用insertElemInList时,useEffect触发并开始工作n次。 我不知道insertElemInList()的调用次数是多少,每次插入多少元素,因为此方法是在用户在页面上执行某些操作后调用的。 因此,如果用户两次或更多次调用insertElemInList(),则在效果的最后一次迭代完成之前,效果触发的方式是错误的,实际上它会由于{{ 1}},但它本身也会导致更多的迭代和错误的行为。

所以我试图弄清楚如何在效果内部使用某些不会触发效果本身但可以正确使用的东西。 例如,我正在考虑修改效果和添加状态

insertElemInList

,然后继续使用const [semWait, setSem] = useState(1)方法更新list状态,但是现在:

insertElemInList()

上面的代码只是我如何解决问题的示例,我认为这不是最好的解决方案,我为您提供了这个示例,只是为了让您了解我想做什么。 但是,正如您所看到的,我可以在 useEffect( () => { let doSomething = () => { if(list.length < 0) return //Do Something with the first element of the list //Remove first element from the list if(list.length > 0) doSomething() } doSomething() setSem(1) }, [semWait]) insertElemInList = () => { //insert one or more elements in list if(semWait == 1) setSem(0) } 的状态中添加任意数量的值,并仅在尚未激活时才触发效果(换句话说,仅当信号量为由效果本身重置)。但是,我知道在效果中使用状态而不将其包含在依赖关系中不是一件好事,并且如果我将状态insertElemInList()添加为list的依赖关系,则会出现问题。

问题在于,如果不将其包含在依赖项中,我将无法弄清楚useEffect中的值

编辑: 抱歉,我的回复很晚,我尝试自己执行此代码,但是我的工作中存在工作流问题,我将尝试解释这些问题:

该代码段是从API下载某些文件的代码段,站点上的用户具有要下载的文件列表,他可以单击这些文件以根据需要多次下载。我的目的是创建一个请求队列,以免向服务器发送太多请求。

以下代码显示了我的工作,我插入了一些注释,以使您了解我的代码应如何工作:

useEffect

现在的问题是,当我尝试重置效果中的队列状态时,我调用:

const [queue, setQueue] = useState({
  "op_name": "NO_OP"
})

//file download function
let requestFileDownload = (fileId) => {

  /*
  This function construct the object to put in queue state and call the method 'insertInQueue'
  */

  let workState = appState

  insertInQueue({
    "op_name": "DOWNLOAD_FILE",
    "file_id": fileId,
    "username": workState.user.username,
    "token": workState.user.token
  })
}


//Function insertInQueue to insert an element in the queue
let insertInQueue = (objQueue) => {
    
  //Some control to check if the object passed exist, and have valid fields
  if (!objQueue || !objQueue.op_name || objQueue.op_name === "NO_OP") return //nothing to insert in queue

  //calling method to insert in timeline div, this work only whith front-end dom elements (full synchronous)
  insertElemInTimeline(objQueue.op_name)

    //setting timeout in which try to insert the object passed in queue
  setTimeout(function run() {
    let workQueue = queue //gettind queue object
    if (workQueue && workQueue.op_name === "NO_OP") {
        /*
      if queue exist and the op_name is "NO_OP", this mean that the previus operations on the queue
      is finished, so we can start this operation
      */
      setQueue(objQueue) //set the queue with the object passed as paramether to trigger the effect
      return;
    }

    // if the queue op_name was != "NO_OP" call the function again for retry to insert in 1 second
    setTimeout(run, 1000)
  }, 0)
}



//Effect triggered when queue object change
useEffect(() => {

  if (queue.op_name === "NO_OP") return //no operation to do

    //Effective file download
  let downloadFileEffect = async () => {

    let objQueue = queue //getting queue state

        //Two functions to download the element by calling backend api
    let downloadFileResponse = await downloadFile(objQueue.file_id, objQueue.username, objQueue.token)
    download(downloadFileResponse.data, downloadFileResponse.headers['x-suggested-filename'])

        //after the method have completed, i can set a new state for the queue with "op_name": "NO_OP"
    let appoStateQueue = {
      "op_name": "NO_OP"
    }
    setQueue(appoStateQueue)

        //method for remove the element from the dom
    removeElemFromTimeline()
  }

    //calling function to trigger the donwload.
  downloadFileEffect()

}, [queue])

如果用户在第一次运行后单击两次下载,则不会重置队列。 实际上,队列停止时插入了第一个对象,并且没有被效果重置,因此第二次下载永远不会开始,因为它将永远看到第一次下载所占用的队列。

如果用户单击一个下载,然后等待下载,然后单击第二个,则没有问题,并且队列已被效果正确重置

1 个答案:

答案 0 :(得分:0)

首先,useEffect不会运行“触发本身n次,其中n是列表中元素的编号”。 useEffect将在list每次更改长度或驻留在与先前渲染中不同的内存空间中时运行。这就是“浅”比较与React中的javascript对象一起工作的方式。您的主要问题是您正在从效果内部更改依赖性。这意味着在效果运行时,它会更新依赖关系并强制其一次又一次地运行...内存泄漏!

您的解决方案可能有效,但是正如您所说,这不是最佳实践。更好的解决方案(imo)将允许“ parsedList”状态,该状态可能是解析列表的最终结果。让清单中的真相来源仅受客户端交互影响。您可以监视这些更改,并根据这些更改更改parsedList