【问题标题】:React setState callback doesn't re-render a quickly toggled stateReact setState 回调不会重新呈现快速切换的状态
【发布时间】:2020-04-07 00:12:23
【问题描述】:

假设我有一段逻辑(例如搜索栏),我想在这段时间内显示一个微调器。所以我有以下代码:

this.state = {
    searching: false,
}

invokeSearch() {
    this.setState({ searching: true })

    const fuse = new Fuse(arrayOfData, { keys: [name: 'title'] })
    const searchResults = fuse.search(searchTerm)

    this.setState({ searching: false })
}

我将 searching 布尔值存储在状态中,以便我可以将它传递给组件以显示或隐藏微调器。

我知道这不起作用,因为setState 是异步的,因此根据文档,我可以改用回调。

setState() 的第二个参数是一个可选的回调函数,一旦 setState 完成并重新渲染组件,就会执行该回调函数。通常我们建议使用 componentDidUpdate() 来代替此类逻辑。

所以我的代码现在看起来像这样:

this.state = {
    searching: false,
}

invokeSearch() {
    this.setState({ searching: true }, () => {
        const fuse = new Fuse(arrayOfData, { keys: [name: 'title'] })
        const searchResults = fuse.search(searchTerm)

        this.setState({ searching: false })
    })
}

但是,这并没有按预期工作。将 this.state.searching 渲染为 <Text> 组件或在某些三元逻辑中显示/隐藏微调器不会明显地从 false 更改。

我猜这与 setState 的异步批处理性质有关,这导致它不起作用?

为了解决这个问题,我一直在使用以下解决方案,对我来说这似乎是一个 hack:

this.state = {
    searching: false,
}

invokeSearch() {
    this.setState({ searching: true }

    setTimeout(() => {
        const fuse = new Fuse(arrayOfData, { keys: [name: 'title'] })
        const searchResults = fuse.search(searchTerm)

        this.setState({ searching: false })
    }, 0)
}

谁能向我解释为什么回调不能按我预期的那样工作,以及如何以更优雅的方式解决这个问题?

编辑:修正问题

感谢您提供的所有有见地的答案和 cmets。进一步调查后,我觉得 Fuse 不是这个问题的好例子。我还有另一个异步 Redux 示例,我将在下面描述

当我点击一个按钮时,我的目标是显示一个微调器,更新 Redux 存储(它反过来重新呈现项目列表),然后隐藏微调器。 Redux 使用的是 Thunk,所以它是异步的,我在操作上有一个回调设置。 reducer 没有做任何花哨的事情,只是用新的数据值更新 Redux 存储。

// action
export function updateFilters(filters, successCallback = () => {}) {
    return (dispatch) => {
        dispatch({ type: ACTIONS.UPDATE_FILTERS, data: filters })
        successCallback()
    }
}
// component
changeFilter = (filters) => {
    this.setState(() => ({
        loading: true,
    }), () => {
        updateFilters(filters, () => {
            this.setState({
                loading: false,
            })
        })
    }
} 

微调器仍然没有渲染!

【问题讨论】:

  • 您发布的所有方法都不起作用,因为您正在调用异步函数。还向我们提供异步功能代码。
  • 可以/不应该在异步方法的回调中设置searching: false(只要它实际完成)?
  • 根据代码的样子,你的搜索函数应该返回一个Promise,然后你应该在 Promise 解决/拒绝后更新状态:fuse.search(searchTerm).then(() => this.setState({ searching: false }))
  • @kinduser 正如我所说,最后一种方法确实有效。
  • @scgough 抱歉,我正在使用我认为实际上是同步的 Fuse.js。我已经更新了我的问题。

标签: javascript reactjs react-native react-state-management react-state


【解决方案1】:

异步状态更新

如果您想确保通过 setState 更新您的状态,您可以传递一个函数而不是文档中所述的对象:

要修复它,请使用 setState() 的第二种形式,它接受函数而不是对象。该函数将接收先前的状态作为第一个参数,并将应用更新时的道具作为第二个参数

State Updates May Be Asynchronous

例如:

this.setState(prevState => ({ searching: !prevState.searching }));

或者

this.setState(() => ({ searching: true }));

处理你的异步函数调用

你可以使用async/awaitasync function

它的一个好处是它暂停异步函数的执行直到返回的承诺解决/拒绝然后你确定你可以更新你的@再987654327@很方便

【讨论】:

  • 谢谢。你能看看我修改后的问题吗?我试图传递一个函数而不是一个对象,但它仍然拒绝渲染微调器。
【解决方案2】:

编辑:看起来您是在操作中定义回调:

export function updateFilters(filters, successCallback = () => {}) {

而不仅仅是定义引用回调的变量:

export function updateFilters(filters, successCallback) {

第一个问题的答案: 仍然感觉像是在设置比赛条件

第一个动作:

this.setState({ searching: true }

被添加到异步堆栈中

(在您的“hackey”解决方案中) 然后将下一项添加到异步堆栈中,即超时。恰好是初始 setState 在您的超时之前解决。在 JS 中,不能保证什么会首先从异步堆栈中出来。

我通常的设计模式是: setState 加载-> 在回调中执行异步调用-> 在其中解决异步内容然后 setState。

在异步等待语法中更容易阅读:

const invokeSearch = async () =>{

   try{
      //set loading state

      this.setState({ searching: true }
      // if you wanted to make certain you weren't setting up a race condition
      // you could wrap the above in a promise and await the result of that. 
      // that seems a bit overkill and harder to read. 


      // await the results of your search. This will make sure that you don't
      // proceed until you have the results

      const response = await async stuff

      if(response.success){

         // do some stuff with response 
         // then set the new state to searching false as your last step
         this.setState({ searching: false })

      }else{
        //handle errors
      }
   } catch(err){
     console.error(err)
   }
}

作为获取:

handleSearch(){
     fetch()
    .then(r => r.json)
    .then(data =>{
        //do something with data
        this.setState({searching: false})
     }
}

invokeSearch() {
    this.setState({ searching: true }, handleSearch)
}

另一个可能的问题是,我上次阅读 setState 上的文档时,它会强制重新呈现页面,所以我不确定这是否也是您的问题。

这是一个完整的反应组件,如果有帮助,可以测试和工作:

import React from "react";

class StarWarsLuke extends React.Component {
  state = {
    searching: false,
    name: ""
  };

  handleSearch = () => {
    fetch("https://swapi.co/api/people/1")
      .then(r => r.json())
      .then(data => {
        console.log(data.name);
        this.setState({ searching: false, name: data.name });
      });
  };

  startSearch = () => {
    this.setState({ searching: true }, this.handleSearch);
  };

  render() {
    return (
      <div>
        <p>
          Am I searching?
          {this.state.searching ? "  searching" : "  not searching"}
        </p>
        {this.state.name ? (
          <p>{this.state.name}</p>
        ) : (
          <p>I don't have a name yet</p>
        )}
        <button onClick={this.startSearch}>get my name</button>
      </div>
    );
  }
}

export default StarWarsLuke;

【讨论】:

    【解决方案3】:

    您是否尝试过将更新放入 setState 回调?比如:

    invokeSearch() {
        this.setState({ searching: true }, () => {
           // do async search
           this.setState({ searching: false })
        }
    }
    

    【讨论】:

      【解决方案4】:

      使函数invokeSearch 异步并使用await。这是修改后的代码。

      async invokeSearch() {
          this.setState({ searching: true })
      
          const fuse = new Fuse(arrayOfData, { keys: [name: 'title'] })
          const searchResults = await fuse.search(searchTerm)
      
          this.setState({ searching: false })
      }
      

      它应该修复它。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-01-28
        • 2021-10-10
        • 1970-01-01
        • 2020-10-25
        • 2022-12-09
        • 2020-06-21
        • 2020-01-30
        相关资源
        最近更新 更多