简体   繁体   English

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

[英]React component not re-rendering on state change

I have a React Class that's going to an API to get content.我有一个 React Class,它将转到 API 以获取内容。 I've confirmed the data is coming back, but it's not re-rendering:我已经确认数据正在返回,但它没有重新渲染:

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>
    );
  }
});

However, if I add a debugger like below, newDeals are populated, and then once I continue, i see the data:但是,如果我添加如下所示的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 });
  },

This is what's calling deals list:这是所谓的交易列表:

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

I'd like to add to this the enormously simple, but oh so easily made mistake of writing:我想添加一个非常简单但很容易出错的写作错误:

this.state.something = 'changed';

... and then not understanding why it's not rendering and Googling and coming on this page, only to realize that you should have written: ...然后不明白为什么它不渲染和谷歌搜索并来到这个页面,才意识到你应该写:

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

React only triggers a re-render if you use setState to update the state.如果您使用setState更新状态,React 只会触发重新渲染。

That's because the response from chrome.runtime.sendMessage is asynchronous;那是因为来自chrome.runtime.sendMessage的响应是异步的; here's the order of operations:这是操作顺序:

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 });

When you pause the script with the debugger, you're giving the extension time to call the callback;当你用调试器暂停脚本时,你给了调用回调的扩展时间; by the time you continue, the data has arrived and it appears to work.当您继续时,数据已经到达并且似乎可以工作。

To fix, you want to do the setState call after the data comes back from the Chrome extension:要修复,您希望在数据从 Chrome 扩展程序返回后执行setState调用:

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)

[Edit] [编辑]

If this doesn't work for you, check out the other answers on this question, which explain other reasons your component might not be updating.如果这对您不起作用,请查看此问题的其他答案,其中解释了您的组件可能无法更新的其他原因。

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

My scenario was a little different.我的情况有点不同。 And I think that many newbies like me would be stumped - so sharing here.而且我认为很多像我这样的新手都会被难住 - 所以在这里分享。

My state variable is an array of JSON objects being managed with useState as below:我的状态变量是一个由 useState 管理的 JSON 对象数组,如下所示:

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

However when update the toCompare with setToCompare as in the below function - the re-render won't fire.但是,当使用 setToCompare 更新 toCompare 时,如下面的函数所示 - 不会触发重新渲染。 And moving it to a different component didn't work either.并将其移动到不同的组件也不起作用。 Only when some other event would fire re-render - did the updated list show up.只有当其他一些事件会触发重新渲染时 - 更新的列表才会出现。

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

This was the solution for me.这是我的解决方案。 Basically - assigning the array was copying the reference - and react wouldn't see that as a change - since the ref to the array isn't being changed - only content within it.基本上 - 分配数组是复制引用 - 并且反应不会将其视为更改 - 因为数组的引用没有被更改 - 只有其中的内容。 So in the below code - just copied the array using slice - without any change - and assigned it back after mods.所以在下面的代码中 - 只是使用切片复制了数组 - 没有任何更改 - 并在 mods 之后将其分配回来。 Works perfectly fine.工作得很好。

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

Hope it helps someone like me.希望它可以帮助像我这样的人。 Anybody, please let me know if you feel I am wrong - or there is some other approach.任何人,如果您觉得我错了,请告诉我 - 或者有其他方法。

Thanks in advance.提前致谢。

To update properly the state, you shouldn't mutate the array.要正确更新 state,您不应改变数组。 You need to create a copy of the array and then set the state with the copied array.您需要创建数组的副本,然后使用复制的数组设置 state。

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);
    }

In my case, I was calling this.setState({}) correctly, but I my function wasn't bound to this, so it wasn't working.就我而言,我正确地调用了this.setState({}) ,但我的函数没有绑定到 this,所以它不起作用。 Adding .bind(this) to the function call or doing this.foo = this.foo.bind(this) in the constructor fixed it..bind(this)添加到函数调用或在this.foo = this.foo.bind(this)函数中执行this.foo = this.foo.bind(this)修复它。

I was going through same issue in React-Native where API response & reject weren't updating states我在 React-Native 中遇到了同样的问题,其中 API 响应和拒绝没有更新状态

apiCall().then(function(resp) { this.setState({data: resp}) // wasn't updating }

I solved the problem by changing function with the arrow function我通过用箭头函数改变function解决了这个问题

apiCall().then((resp) => {
    this.setState({data: resp}) // rendering the view as expected
}

For me, it was a binding issue.对我来说,这是一个有约束力的问题。 Using arrow functions solved it because arrow function doesn't create its's own this , its always bounded to its outer context where it comes from使用箭头函数解决了这个问题,因为箭头函数不会创建自己的this ,它总是绑定到它来自的外部上下文

我的问题是当我应该使用“React.Component”时我使用了“React.PureComponent”。

I was updating and returning the same object passed to my reducer.我正在更新并返回传递给我的减速器的相同对象。 I fixed this by making a copy of the element just before returning the state object like this.我通过在像这样返回状态对象之前制作元素的副本来解决这个问题。

Object.assign({}, state)

After looking into many answers (most of them are correct for their scenarios) and none of them fix my problem I realized that my case is a bit different:在查看了许多答案(其中大多数对于他们的场景是正确的)并且没有一个解决我的问题后,我意识到我的情况有点不同:

In my weird scenario my component was being rendered inside the state and therefore couldn't be updated.在我奇怪的场景中,我的组件在状态内呈现,因此无法更新。 Below is a simple example:下面是一个简单的例子:

constructor() {
    this.myMethod = this.myMethod.bind(this);
    this.changeTitle = this.changeTitle.bind(this);

    this.myMethod();
}

changeTitle() {
    this.setState({title: 'I will never get updated!!'});
}

myMethod() {
    this.setState({body: <div>{this.state.title}</div>});
}

render() {
    return <>
        {this.state.body}
        <Button onclick={() => this.changeTitle()}>Change Title!</Button>
    </>
}

After refactoring the code to not render the body from state it worked fine :)重构代码以从状态渲染主体后,它工作正常:)

If someone is here for similar problem, but using React Functional components rather class components AND also using react reducer, --> Move your api call outside of the reducer.如果有人在这里遇到类似问题,但使用 React Functional 组件而不是 class 组件并且还使用 React reducer,--> 将 api 调用移到 reducer 之外。 Reducer should never do an api call. Reducer 永远不应该调用 api。 Refer to https://stackoverflow.com/a/39516485/12121297 for detailed response详细回复参考https://stackoverflow.com/a/39516485/12121297

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM