当状态数组中的任何值被修改时,ReactJS 组件状态不会更新

Posted

技术标签:

【中文标题】当状态数组中的任何值被修改时,ReactJS 组件状态不会更新【英文标题】:ReactJS component state is not updating when any value in state array is modified 【发布时间】:2019-06-03 19:55:53 【问题描述】:

在我的 React 组件中,我希望我的状态能够自动更新而无需重新加载页面。我正在使用 lodash.iseqaual 方法来比较状态变化,但这不会更新状态。

下面是我的组件:

import React,  Component  from "react";
import  Link  from "react-router-dom";
import axios from "axios";
import $ from "jquery";
import isEqual from "lodash/isEqual";
$.DataTable = require("datatables.net");

class Active extends Component 
  state = 
    activeData: [],
    flag: 0,
    isLoading: true
  ;

  componentDidMount() 
    this.getActiveData();
  

  componentDidUpdate(prevProps, prevState) 
    if (!isEqual(prevState.activeData, this.state.activeData)) 
      this.getActiveData();
    
  

  getActiveData() 
    const params = new FormData();
    params.append("status", "active");
    axios.post("http://127.0.0.1:8000/details/", params).then(res => 
      if (res.data.result === 1) 
        this.setState(
          activeData: res.data.data,
          flag: 1,
          isLoading: false
        );
        if (!this.state.isLoading) 
          this.initTable();
        
       else if (res.data.result === 0) 
        this.setState( isLoading: false );
        this.initTable();
      
    );
  

  initTable() 
    $("#Active_Datatable").DataTable();
  

  render() 
    if (this.state.isLoading) 
      return (
        <img
          
          src="https://upload.wikimedia.org/wikipedia/commons/b/b1/Loading_icon.gif"
        />
      );
    
    return (
      <React.Fragment>
        <div className="panel-heading" role="tab" id="heading3">
          <a
            href="#Current"
            className="collapsed text-left textuppercase"
            role="button"
            data-toggle="collapse"
            data-parent="tabs-content"
            aria-expanded="true"
            aria-controls="Current"
          >
            <i className="fas fa-list-ul" /> Current
            <i className="fas fa-chevron-down pull-right" />
          </a>
        </div>

        <div
          id="Current"
          role="tabpanel"
          className="tab-pane panel-collapse collapse"
          aria-labelledby="heading3"
        >
          <table
            id="Active_Datatable"
            className="display"
            style= width: "100%" 
          >
            <thead>
              <tr>
                <th>#</th>
                <th>Name</th>
                <th>Open</th>
                <th>Close</th>
                <th>Price</th>
              </tr>
            </thead>
            this.state.flag === 1 ? (
              <tbody>
                this.state.activeData.map((ac, i) => (
                  <tr key=sc.id>
                    <td className="text-center">i + 1</td>
                    <td>
                      <Link
                        to=
                          pathname: `/$ac.name_slug/`,
                          state: 
                            id: ac.id,
                            name: ac.name
                          
                        
                      >
                        sc.name
                      </Link>
                    </td>
                    <td>sc.open_date</td>
                    <td>sc.close_date</td>
                    <td>
                      Number(sc.price).toLocaleString("en-IN", 
                        currency: "INR"
                      )
                    </td>
                  </tr>
                ))
              </tbody>
            ) : null
          </table>
        </div>
      </React.Fragment>
    );
  


export default Active;

componentDidUpdate() 方法中的 lodash.isequal 没有更新我的状态。

我也尝试过componentDidUpdate中的以下逻辑:

componentDidUpdate(prevProps, prevState) 
    if (prevState.activeData !== this.state.activeData) 
      this.getActiveData();
    

但这会进入无限循环,并且会增加 API 调用,即使在没有变化之后也是如此。

我怎样才能做到这一点?

提前致谢!

**需要等待这个问题的解决方案。

更新:

从所有答案中,我想我应该用示例来解释。因此,当我进行 API 调用并更新状态 activeData 时,它更新为:

[
 
   id: 1
   name: "ABC"
   close_date: "2019-01-11"
   open_date: "2019-01-08"
   price: "80"
 ,
 
   id: 2
   name: "XYZ"
   close_date: "2019-01-12"
   open_date: "2019-01-04"
   price: "80"
 
]

例如,如果我将名称字段从 ABC 更新为 ABCD,则返回的结果将是:

[
 
   id: 1
   name: "ABCD"
   close_date: "2019-01-11"
   open_date: "2019-01-08"
   price: "80"
 ,
 
   id: 2
   name: "XYZ"
   close_date: "2019-01-12"
   open_date: "2019-01-04"
   price: "80"
 
]

然后组件中的名称值应该会自动从ABC更新为ABCD,而不需要重新加载页面。

shouldComponentUpdate 或按照建议更改我的 lodash 语法都不会发生这种情况。

目录结构:

project
  |- public
  |- src
     -components
        -Header_Footer
          Header.jsx
          Footer.jsx
        -Home
          index.jsx
          -Details
             Active.jsx
             Upcoming.jsx
     App.js
     index.js    

我还要说明我的组件是如何渲染的:

Details.jsx

import React,  Component  from "react";
import Active from "./Active";
import Upcoming from "./Upcoming";

class Details extends Component 

  render() 
    return (
      <div className="col-md-9 col-sm-9 col-xs-12">
        <div className="right_panel">
          <h2>Listing</h2>

          <div className="responsive-tabs text-center ">
            <ul className="nav nav-tabs" role="tablist">
              <li role="presentation" className="active">
                <a
                  href="#Upcoming"
                  aria-controls="Upcoming"
                  role="tab"
                  data-toggle="tab"
                >
                  Upcoming
                </a>
              </li>
              <li role="presentation" className="">
                <a
                  href="#Current"
                  aria-controls="Current"
                  role="tab"
                  data-toggle="tab"
                >
                  Current
                </a>
              </li>

            </ul>
            <div
              id="tabs-content"
              className="tab-content panel-group table-responsive"
            >
              <Upcoming />
              <Active />
            </div>
          </div>
        </div>
      </div>
    );
  


export default Details;

Home.js

import React,  Component  from "react";
import Sidebar from "./Sidebar";
import Details from "./details";

class Home extends Component 

  render() 
    return (
      <div className="container container_padding">
        <div className="row">
          <Sidebar />
          <Details />
        </div>
      </div>
    );
  


export default Home;

App.js

import React,  Component  from "react";
import  Router, Route, Switch  from "react-router-dom";
import Header from "./components/Header_Footer/Header";
import Footer from "./components/Header_Footer/Footer";
import Home from "./components/Home";
import createBrowserHistory from "history/createBrowserHistory";
const history = createBrowserHistory();

class App extends Component 
  render() 
    return (
      <Router history=history>
        <React.Fragment>
          <Header />
          <Switch>
            <Route exact path="/" component=Home />
          </Switch>
          <Footer />
        </React.Fragment>
      </Router>
    );
  


export default App;

【问题讨论】:

不要使用componentDidUpdate,如果使用最新版本,可以使用componentWillReceivePropsgetderivedState @Justcode componentWillReceiveProps 也没有这样做。我的反应版本是 16 请创建 stackblitz 链接以重现问题,并附上示例数据 @Justcode 我无法创建 stackblitz 链接,因为 dusring API 调用它说:net::ERR_SSL_PROTOCOL_ERROR。我有 AWS EC2 服务器,我从哪里调用我的 API 【参考方案1】:

componentDidUpdate 中的getActiveData 只会被调用一次,在第一次 componentDidUpdate 发生时,它永远不会被再次调用,因为prevState.activeData 永远不会改变。

你可以试试其他方法,在componentDidUpdatecomponentDidUpdate上触发getActiveData(),使用shouldComponentUpdate来决定组件是否应该更新,这里是代码

componentDidMount() 
    this.getActiveData();


shouldComponentUpdate(nextProps, nextState)
   return !isEqual(nextState.activeData, this.state.activeData)


componentDidUpdate() 
    this.getActiveData();

【讨论】:

试过了,但现在this.getActiveData(); 永远不会更新。我没有看到任何记录。它一直在加载。 @ReemaParakh 对不起,我忘记返回 true,只需更新答案,您可以再试一次吗 能否请您检查我更新的问题。我试图用示例来解释,因为shouldComponentUpdate() 没有更新值。【参考方案2】:
isEqual method is working Properly, You have need to change componentDidUpdate() method from getSnapshotBeforeUpdate() method because componentDidUpdate(prevProps, prevState) method execute after update the state so prevState having the same value which are having the this.state.activeData.

getSnapshotBeforeUpdate(prevProps, prevState) 
    if (!isEqual(prevState.activeData, this.state.activeData)) 
      this.getActiveData();
    
    return null;
  

【讨论】:

做到了。但是控制台说-index.js:1446 Warning: Active: getSnapshotBeforeUpdate() should be used with componentDidUpdate(). This component defines getSnapshotBeforeUpdate() only. 并且状态也没有更新。 getSnapshotBeforeUpdate() 方法是 react:16.3 的一部分,请检查反应版本。 这不起作用的任何原因? if (prevState.activeData !== this.state.activeData) this.getActiveData(); 如果 activeData 在多个对象和数组中包含对象,则 !== 符号不能与多个对象和数组的值匹配,它将始终显示不同并且也匹配该引用财产。就像 activeData:id:2323, users[userId:"323", name:''"xyz", children: , userId:"323", name:''"xyz", children: ] 好的,那我将如何管理状态变化,即使lodash isEqual 在这里也不起作用。【参考方案3】:
 you can also use the lodash package from this 
 https://www.npmjs.com/package/lodash

 $ npm i --save lodash        

 import isEqual from "lodash/isEqual";

 componentDidUpdate(prevProps, prevState) 
   if (!isEqual(prevState.activeData, this.state.activeData)) 
      this.getActiveData();
   
 

【讨论】:

isEqual 方法工作正常,您需要从 getSnapshotBeforeUpdate() 方法更改 componentDidUpdate() 方法,因为 componentDidUpdate(prevProps, prevState) 方法在更新状态后执行,因此 prevState 具有相同的值this.state.activeData getSnapshotBeforeUpdate(prevProps, prevState) if (!isEqual(prevState.activeData, this.state.activeData)) this.getActiveData(); 返回空值; 使用此方法代替 componentDidUpdate(prevProps, prevState)【参考方案4】:

我想你有一些 Lodash Docs 的语法错误,你需要在函数之前添加下划线。

尝试如下操作:

if (!_.isEqual(prevState.activeData, this.state.activeData)) 
      this.getActiveData();
    

另外,请检查两个数组是否唯一,以及当前是否正在进行比较。这就是你的问题所在。

【讨论】:

我试过这个。但是componentDidUpdate() 方法没有被调用。我在做console.log("update"),它只打印2次。如果 DB 中的值发生更改并且状态已更新,则它不会捕获更新。它没有帮助。

以上是关于当状态数组中的任何值被修改时,ReactJS 组件状态不会更新的主要内容,如果未能解决你的问题,请参考以下文章

重新渲染 Reactjs 数组组件

如何在状态更改(父)reactjs时重新渲染子组件

当reducer在我的功能组件reactjs中出错时,我想清除或初始状态

重新渲染 Reactjs 后,父组件立即失去状态

React Js - Flux 中的状态管理

从 ReactJS 中的数组中删除复杂组件