React 组件不会在状态更改时重新渲染

IT技术 javascript reactjs
2021-04-08 00:36:38

我有一个 React 类,它将通过 API 获取内容。我已经确认数据回来了,但它没有重新渲染:

var DealsList = React.createClass({
  getInitialState: function() {
    return { deals: [] };
  },
  componentDidMount: function() {
    this.loadDealsFromServer();
  },
  loadDealsFromServer: function() {
    var newDeals = [];

    chrome.runtime.sendMessage({ action: "findDeals", personId: this.props.person.id }, function(deals) {
      newDeals = deals;
    });

    this.setState({ deals: newDeals });
  },
  render: function() {
    var dealNodes = this.state.deals.map(function(deal, index) {
      return (
        <Deal deal={deal} key={index} />
      );
    });
    return (
      <div className="deals">
        <table>
          <thead>
            <tr>
              <td>Name</td>
              <td>Amount</td>
              <td>Stage</td>
              <td>Probability</td>
              <td>Status</td>
              <td>Exp. Close</td>
            </tr>
          </thead>
          <tbody>
            {dealNodes}
          </tbody>
        </table>
      </div>
    );
  }
});

但是,如果我在debugger下面添加一个类似的内容,newDeals则填充,然后一旦我继续,我就会看到数据:

  loadDealsFromServer: function() {
    var newDeals = [];

    chrome.runtime.sendMessage({ action: "findDeals", personId: this.props.person.id }, function(deals) {
      newDeals = deals;
    });
    debugger
    this.setState({ deals: newDeals });
  },

这就是所谓的交易清单:

var Gmail = React.createClass({
  render: function() {
    return (
      <div className="main">
        <div className="panel">
          <DealsList person={this.props.person} />
        </div>
      </div>
    );
  }
});
6个回答

我想添加一个非常简单但很容易出错的写作错误:

this.state.something = 'changed';

...然后不明白为什么它不渲染和谷歌搜索并来到这个页面,才意识到你应该写:

this.setState({something: 'changed'});

如果您用于setState更新状态,React 只会触发重新渲染

@StijndeWitt 我登录到 Stackoverflow 来支持你的答案。谢谢兄弟;D
2021-05-27 00:36:38
这正是我遇到的问题。奇怪的是他们没有抛出警告,因为他们在尝试更新道具时抛出了一个警告。
2021-06-10 00:36:38
初学者理解和记住的非常重要的概念。
2021-06-12 00:36:38
@AndrewJM 他们不能发出警告。他们可以,如果你写,this.state = 'something'因为你会点击 setter for state,但在上面的例子中,代码点击 getter,它返回一个对象,然后它最终在一个对象上设置一个字段,该字段只是状态。
2021-06-16 00:36:38
@TheBigCheese 最好在上面写一个(好)问题。有些人可以提供帮助,但不是在另一个问题的评论中。
2021-06-21 00:36:38

那是因为来自的响应chrome.runtime.sendMessage是异步的;这是操作顺序:

var newDeals = [];

// (1) first chrome.runtime.sendMessage is called, and *registers a callback*
// so that when the data comes back *in the future*
// the function will be called
chrome.runtime.sendMessage({...}, function(deals) {
  // (3) sometime in the future, this function runs,
  // but it's too late
  newDeals = deals;
});

// (2) this is called immediately, `newDeals` is an empty array
this.setState({ deals: newDeals });

当你用调试器暂停脚本时,你给了调用回调的扩展时间;当您继续时,数据已经到达并且它似乎起作用了。

要修复,您希望setState在数据从 Chrome 扩展程序返回后进行调用:

var newDeals = [];

// (1) first chrome.runtime.sendMessage is called, and *registers a callback*
// so that when the data comes back *in the future*
// the function will be called
chrome.runtime.sendMessage({...}, function(deals) {
  // (2) sometime in the future, this function runs
  newDeals = deals;

  // (3) now you can call `setState` with the data
  this.setState({ deals: newDeals });
}.bind(this)); // Don't forget to bind(this) (or use an arrow function)

[编辑]

如果这对您不起作用,请查看此问题的其他答案,其中解释了您的组件可能无法更新的其他原因。

呸!当调试器线修复它时,我应该知道这是这种情况。bind(this) 是我第一次尝试时错过的东西。谢谢,非常详细的评论!
2021-06-11 00:36:38

我的情况有点不同。而且我认为很多像我这样的新手都会被难住 - 所以在这里分享。

我的状态变量是一个由 useState 管理的 JSON 对象数组,如下所示:

const [toCompare, setToCompare] = useState([]);

但是,当使用 setToCompare 更新 toCompare 时,如下面的函数所示 - 不会触发重新渲染。并将其移动到不同的组件也不起作用。只有当其他一些事件会触发重新渲染时 - 更新的列表才会出现。

const addUniversityToCompare = async(chiptoadd) =>
  {
      var currentToCompare = toCompare;
      currentToCompare.push(chiptoadd);
      setToCompare(currentToCompare);
  }

这是我的解决方案。基本上 - 分配数组是复制引用 - 并且react不会将其视为更改 - 因为数组的引用没有被更改 - 只有其中的内容。所以在下面的代码中 - 只是使用切片复制了数组 - 没有任何更改 - 并在 mods 之后将其分配回来。工作得很好。

const addUniversityToCompare = async (chiptoadd) => {
    var currentToCompare = toCompare.slice();
    currentToCompare.push(chiptoadd);
    setToCompare(currentToCompare);
}

希望它可以帮助像我这样的人。任何人,如果您觉得我错了,请告诉我 - 或者有其他方法。

提前致谢。

哇,我的问题几乎是副本。谢谢!
2021-05-30 00:36:38
是的,米登,没错。我同意。您认为更新我的解决方案帖子是否有意义 - 将这一点作为更好的方式包含在内 - 所以人们将其用作解决此问题的更通用的方法?很高兴有帮助。
2021-06-08 00:36:38
极好的。我有完全相同的问题。
2021-06-10 00:36:38
我也是。在 ES6 中,我们可以使用扩展运算符来克隆状态以提示 React 确认状态更改:let newItems = [...items];
2021-06-14 00:36:38
哈哈,这里也有抄送问题。有趣的!
2021-06-16 00:36:38

另一个非常简单的错误,这对我来说是问题的根源:我编写了自己的shouldComponentUpdate方法,它没有检查我添加的新状态更改。

就我而言,我调用this.setState({})正确,但我的函数没有绑定到这个,所以它不起作用。添加.bind(this)到函数调用或this.foo = this.foo.bind(this)在构造函数中执行修复它。

我有这个问题。所有函数都在构造函数中正确绑定。
2021-05-27 00:36:38