【问题标题】:ReactJS state updates one step behindReactJS 状态更新落后一步
【发布时间】:2021-04-09 08:15:48
【问题描述】:

我正在尝试使用 ReactJS 创建一个简单的表格来显示用户信息。下面是一般代码结构的样子:

class ParentComponent extends React.Component {
  state = { 
    data : [] 
  }

  componentDidMount() {
    // initializes state with data from db
    axios.get("link/").then(res => {
      this.setState({data: res.data});
    });

    // I should be able to call this.getData() instead
    // of rewriting the axios.get() function but if I do so,
    // my data will not show up

  }
  
  // retrieves array of data from db
  getData = () => {
    axios.get("link/").then(res => {
      this.setState({data: res.data});
    });
  }  

  render() {
    return (
      <div>
        <ChildComponent data={this.state.data} refetch={this.getData} />
      </div>
    )
  }
}

每个生成的行都应该有一个删除函数,我将根据给定的 id 从数据库中删除条目。删除后,我想从父组件中检索最新的数据重新显示。

class ChildComponent extends React.Component {

  // deletes the specified entry from database
  deleteData = (id) => {
    axios.get("deleteLink/" + id).then(res => {
      console.log(res);
      // calls function from parent component to 
      // re-fetch the latest data from db
      this.props.refetch();

    }).catch(err => {console.log(err)});
  }

  render() {
    let rows = null;
    if(this.props.data.length) {
      // map the array into individual rows
      rows = this.props.data.map(x => {
        return (
          <tr>
            <td>{x.id}</td>
            <td>{x.name}</td>
            <td>
               <button onClick={() => {
                 this.deleteData(x.id)
                }}>
                  Delete
               </button>
            </td>
          </tr>
        )
      })

    }

    return (
      <div>
        <table>
          <thead></thead>
          <tbody>
            {rows}
          </tbody>
        </table>
      </div>
    )
  }
}

我在这里遇到的两个问题是:

  1. 从逻辑上讲,我应该能够从 componentDidMount() 中调用 this.getData(),但如果我这样做,则不会加载表。
  2. 每当我尝试删除一行时,即使条目已从数据库中删除,表也不会反映更新。只有当我刷新页面或删除另一行时,该表才会更新。问题是,组件总是落后 1 次更新。

到目前为止我已经尝试过:

  1. this.forceUpdate() - 不起作用
  2. this.setState({}) - 空 setState 也不起作用
  3. componentDidMount() 更改为componentDidUpdate() - 错误显示我已“达到最大深度”或沿着这条线的东西
  4. 在 axios 前面添加异步等待 - 不起作用

感谢任何帮助。提前致谢。


编辑:我进行了一些调试并追踪了与我的问题无关的问题。我位于 ChildComponent 中的 deleteData() 使用 axios.post() 而不是 axios.get(),我忽略了。

deleteData = (id) => {
    axios.post("deleteLink/", id).then(res => {
      console.log(res);
      // calls function from parent component to 
      // re-fetch the latest data from db
      this.props.refetch();

    }).catch(err => {console.log(err)});
  }

为了让axios.post() 返回响应,为了执行.then(),您需要在路由代码中添加res.json()

【问题讨论】:

  • api调用后能登录res吗?更新状态时看起来也很奇怪,是不是错字?应该是this.setState({data: res.data})
  • 是的,这是我的错字。响应包含通常的标头、数据数组和状态 200 - 这没有什么不寻常的。当我单击删除按钮时,deleteData() 函数确实运行了。但是,在我删除另一行之前,它不会触发组件重新渲染。

标签: reactjs axios jsx


【解决方案1】:

您应该将数据映射到您的孩子。

像这样改变你的父母:

class ParentComponent extends React.Component {
  state = { 
      data : [] 
  }

  componentDidMount() {
      this.getData();
  }
    
  getData = () => axios.get("link/").then(res => this.setState({data: res.data});
    
  deleteData = (id) => axios.get("deleteLink/" + id).then(res => this.getData())
       .catch(err => { console.log(err) });

  render() {
    return (
      <div>
        <table>
          <thead></thead>
          <tbody>
            {this.state.data.map(x => <ChildComponent row={x} deleteData={this.deleteData} />)}
          </tbody>
        </table>
      </div>
    )
  }
}

而你的子组件应该是这样的

const ChildComponent = ({row,deleteData}) => (
      <tr>
        <td>{row.id}</td>
        <td>{row.name}</td>
        <td><button onClick={() => deleteData(row.id)}>Delete</button></td>
      </tr >
    )

【讨论】:

  • 感谢您的回答。我还有一个问题,如果你不介意的话。我想将表格移动到子组件中,因为稍后我可能会添加一些其他组件。是否有一种解决方案可以改为在我的子组件中生成行?我可能希望拥有多个子组件,它们都共享来自父组件的相同数据。我担心的是,即使父母的状态已更新,它们也不会重新呈现。
  • 没问题,用状态变化来响应更新(这是一个法律),如果你的组件没有用state 变化呈现,你应该仔细检查你的结构,也许你的状态不会影响组件。
  • 如果您在制作应用时需要帮助,请使用您的代码创建一个沙箱,这对您有帮助。
  • 感谢您的帮助。我做了一些调试,发现这是一个完全不同的问题导致延迟。这与我的后端和 axios 没有一致地返回响应有关。我设法解决了这个问题,但感谢您的帮助!
  • 很高兴为您提供帮助:)
【解决方案2】:

我在您的代码中找不到问题,我能提供帮助的唯一方法就是告诉您我将如何调试它。

像这样编辑父级:

class ParentComponent extends React.Component {
  state = { 
    data : [] 
  }

  componentDidMount() {
    // You are right when you say this should works, so 
    // stick to it until the bug is fixed
    
    console.log("parent mounted");
    this.getData();
  }
  
  // retrieves array of data from db
  getData = () => {
    console.log("fetching data");

    axios.get("link/").then(res => {
      console.log("fetched data", res.data);
      this.setState({data: res.data});
    });
  }  

  render() {
    return (
      <div>
        <ChildComponent 
          data={this.state.data}
          refetch={this.getData}
        />
      </div>
    )
  }
}

并在子组件中添加这 2 个生命周期方法,仅用于调试目的:

componentDidMount () {
   console.log("child mounted", this.props.data)     
}

componentDidUpdate (prevProps) {
   console.log("old data", prevProps.data);
   console.log("new data", this.props.data);
   console.log("data are equal", prevProps.data === this.props.data);
}

如果您可以分享日志,我可以尝试为您提供更多帮助

【讨论】:

  • 我找到了与代码 sn-p 本身无关的问题。原来我的后端没有始终如一地响应。不过,componentDidUpdate() 部分看起来非常有用。下次遇到错误我会试试的,谢谢。
猜你喜欢
  • 2017-04-23
  • 2017-07-15
  • 2021-05-01
  • 2020-03-26
  • 2021-06-04
  • 2020-08-02
  • 1970-01-01
  • 2019-04-05
相关资源
最近更新 更多