React组件不会在状态更改时重新呈现

bra*_*ert 29 javascript reactjs

我有一个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>
    );
  }
});
Run Code Online (Sandbox Code Playgroud)

但是,如果我添加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 });
  },
Run Code Online (Sandbox Code Playgroud)

这就是所谓的交易清单:

var Gmail = React.createClass({
  render: function() {
    return (
      <div className="main">
        <div className="panel">
          <DealsList person={this.props.person} />
        </div>
      </div>
    );
  }
});
Run Code Online (Sandbox Code Playgroud)

Sti*_*itt 59

我想补充一点非常简单,但是很容易犯错误的写作:

this.state.something = 'changed';
Run Code Online (Sandbox Code Playgroud)

...然后不理解为什么它不是渲染和谷歌搜索并进入这个页面,只是意识到你应该写:

this.setState({something: 'changed'});
Run Code Online (Sandbox Code Playgroud)

如果您使用setState更新状态,则React仅触发重新渲染.

  • @AndrewJM他们不能发出警告。如果您要编写`this.state ='something',他们可能会这样做,因为您可能会碰到“ state”的设置器,但是在上面的示例中,代码会碰到getter,它返回一个对象,然后结束设置对象上的字段,仅是状态的副本。 (2认同)
  • @StijndeWitt 我登录了 Stackoverflow 来支持你的答案。谢谢兄弟;D (2认同)

Tsa*_*v96 16

要正确更新状态,您不应改变数组。您需要创建数组的副本,然后使用复制的数组设置状态。

const [deals, setDeals] = useState([]);
    
   function updateDeals(deal) {
      const newDeals = [...deals]; // spreading operator which doesn't mutate the array and returns new array
      newDeals.push(deal);

      // const newDeals = deals.concat(deal); // concat merges the passed value to the array and return a new array
      // const newDeals = [...deals, deal] // directly passing the new value and we don't need to use push
    
      setDeals(newDeals);
    }
Run Code Online (Sandbox Code Playgroud)


Mic*_*ley 14

那是因为响应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 });
Run Code Online (Sandbox Code Playgroud)

当您使用调试器暂停脚本时,您将给予扩展时间来调用回调; 当你继续时,数据已经到达,它似乎工作.

要解决此问题,您需要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)
Run Code Online (Sandbox Code Playgroud)

[编辑]

如果这对您不起作用,请查看此问题的其他答案,其中说明了您的组件可能未更新的其他原因.


Lyn*_*ynn 14

另一个很容易犯的错误,这是我的问题的根源:我编写了自己的shouldComponentUpdate方法,没有检查我添加的新状态更改.


小智 13

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

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

const [toCompare, setToCompare] = useState([]);
Run Code Online (Sandbox Code Playgroud)

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

const addUniversityToCompare = async(chiptoadd) =>
  {
      var currentToCompare = toCompare;
      currentToCompare.push(chiptoadd);
      setToCompare(currentToCompare);
  }
Run Code Online (Sandbox Code Playgroud)

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

const addUniversityToCompare = async (chiptoadd) => {
    var currentToCompare = toCompare.slice();
    currentToCompare.push(chiptoadd);
    setToCompare(currentToCompare);
}
Run Code Online (Sandbox Code Playgroud)

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

提前致谢。

  • 哇,我的问题几乎是抄袭。谢谢你! (5认同)
  • 我也是。在 ES6 中,我们可以使用展开运算符来克隆状态,以提示 React 确认状态更改:`let newItems = [...items];` (4认同)

der*_*f26 9

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