【问题标题】:Data disappears from the page when viewed查看时数据从页面中消失
【发布时间】:2018-08-13 11:35:15
【问题描述】:

当我想查看它们时,我有一个寄存器列表,当我向下滚动页面并到达末尾之前它们超过 20 个时它们会消失,之后它们就会消失。在这种情况下,控制台中只会出现一个错误。

查看寄存器时出现在浏览器控制台中的错误。

This usually means you called setState() on an unmounted component. 
This is a no-op. Please check the code for the RegistersList component.

list.js

import React, { Component, PropTypes } from 'react';
import { connect }                     from 'react-redux'
import { Link }                        from 'react-router';
import * as utils                      from '../../utils';
import moment                          from 'moment';
import { bindActionCreators }          from 'redux'
import InfiniteScroll                  from 'react-infinite-scroller';
import { index as fetchRegisters }     from '../../actions/registers';

var page = 0

@connect(
  state => ({
    currentFeatures: state.features,
    registers: state.registers.items
  }),
  dispatch => ({
    actions: bindActionCreators({
      fetchRegisters
    }, dispatch)
  })
)
export default class RegistersList extends Component {
  static propTypes = {
    registers: PropTypes.array.isRequired,
    articles: PropTypes.array.isRequired,
    counterparties: PropTypes.array.isRequired,
    handleDestroy: PropTypes.func.isRequired
  };

  constructor(props) {
    super(props);
    this.state = { hasMoreItems: true };
  }

  componentWillReceiveProps(newProps) {
    const { props } = this

    if (props.current !== newProps.current) {
      this.setState({ hasMoreItems: true })
      page = 0
    }
  }

  componentWillMount() {
    page = 0
  }

  fetchRegistersOnScroll() {
    const { actions, current, dispatch } = this.props
    page++

    actions.fetchRegisters(current, page)
      .then(res => {
        dispatch({ type: 'REGISTER/SCROLL', payload: res.data });

        if (res.data.items.length < 20)
          this.setState({ hasMoreItems: false })
      })
  }

  render() {
    const { registers, articles, counterparties, handleDestroy, currentFeatures } = this.props;

    const registersList = registers.map((register, i) => {
      const article = articles.find(a => a.id === register.article_id) || {}
      const typeName = article.type == "Cost" ? 'cost' : 'revenue';
      const counterparty = counterparties.find(c => c.id === register.counterparty_id) || {}
      const client = counterparties.find(c => c.id === register.client_id) || {}
      const manager = counterparties.find(c => c.id === register.sales_manager_id) || {}

      return(
        <tr className="register-table" key={i}>
          <td>{ moment(register.date).format("DD-MM-YYYY") }</td>
          <td>
            {article.title}
            <span className={`register-title-label ${typeName}`}>
              &nbsp;({typeName})
            </span>
          </td>

          { (currentFeatures && currentFeatures.sales) ?
            <td>{ client.name }</td>
          : null }

          { (currentFeatures && currentFeatures.sales) ?
            <td>{ manager.name }</td>
          : null }

          <td>{counterparty ? counterparty.name : '-'}</td>
          <td>{register.value}</td>
          <td><div className="register-note">{register.note}</div></td>
          <td>
            <div className="btn-group btns-hidden" >
               <Link
                  to={`/registers/${register.id}/edit`}
                  className="btn btn-primary btn-sm"
                >
                  <i className="glyphicon glyphicon-pencil"></i>
              </Link>
              <button
                className="btn btn-sm btn-danger"
                onClick={handleDestroy.bind(this, register.id)}
              >
                <i class="fa fa-times" aria-hidden="true"></i>
              </button>
            </div>
          </td>
        </tr>
      )
    })

    if (registers.length) {
      return (
        <InfiniteScroll
          pageStart={0}
          loadMore={this.fetchRegistersOnScroll.bind(this)}
          hasMore={this.state.hasMoreItems}
          loader={<tr className="loader" key={0}><td><b>Loading ...</b></td></tr>}
          element={'tbody'}
        >
          { registersList }
        </InfiniteScroll>
      )
    }

    return(
      <tbody>
        <tr>
          <td rowSpan="6">
            There are no registers...
          </td>
        </tr>
      </tbody>
    );
  }
}

registers.js

import axios from 'axios';
import cookie from 'react-cookie';

const API_URL = `${window.location.origin}/api/v1/registers`;

export function index(params, page) {
  return function(dispatch, getState) {
    return new Promise((resolve, reject) => {
      let headers = {}
      headers['Authorization'] = `Bearer ${cookie.load('token')}`
      headers['workspace-id'] = getState().workspaces.app.current.id
      params.page = page

      axios.get(API_URL, { params, headers })
        .then(res => {
          dispatch({ type: 'REGISTER/FETCH', payload: res.data });
          resolve(res)
        })
        .catch(e => {
          console.error("error: ", e);
          reject(e)
        })
    })
  }
}

export function show(id){
  return function(dispatch, getState) {
    return new Promise((resolve, reject) => {
      let headers = {}
      headers['Authorization'] = `Bearer ${cookie.load('token')}`
      headers['workspace-id'] = getState().workspaces.app.current.id

      axios.get(`${API_URL}/${id}`, { headers: headers })
        .then(res => {
          dispatch({ type: 'REGISTER/SHOW', payload: res.data });
          resolve(res)
        })
        .catch(e => {
          console.error("error: ", e);
          reject(e)
        })
    })
  }
}

export function create(register){
  return function(dispatch, getState) {
    return new Promise((resolve, reject) => {
      let headers = {}
      headers['Authorization'] = `Bearer ${cookie.load('token')}`
      headers['workspace-id'] = getState().workspaces.app.current.id
      let body = {register: register}

      axios.post(API_URL, body, { headers: headers })
        .then(res => {
          dispatch({ type: 'REGISTER/CREATE', payload: res.data });
          resolve(res)
        })
        .catch(e => {
          console.error(e);
          reject(e)
        })
    })
  }
}

export function update(register){
  return function(dispatch, getState) {
    return new Promise((resolve, reject) => {
      let headers = {}
      headers['Authorization'] = `Bearer ${cookie.load('token')}`
      headers['workspace-id'] = getState().workspaces.app.current.id
      let body = {register: register.register}

      axios.patch(`${API_URL}/${register.id}`, body, { headers: headers })
        .then(res => {
          resolve(res)
        })
        .catch(e => {
          console.error("error: ", e);
          reject(e)
        })
    })
  }
}

export function destroy(id){
  return function(dispatch, getState) {
    return new Promise((resolve, reject) => {
      let headers = {}
      headers['Authorization'] = `Bearer ${cookie.load('token')}`
      headers['workspace-id'] = getState().workspaces.app.current.id

      axios.delete(`${API_URL}/${id}`, { headers: headers })
        .then(res => {
          dispatch({ type: 'REGISTER/DELETE', payload: id });
          resolve(res)
        })
        .catch(id => {
          console.error("error", id);
          reject(id)
        })
    })
  }
}

【问题讨论】:

  • 尝试在构造函数 this.fetchRegistersOnScroll=this.fetchRegistersOnScroll.bind(this) 中绑定处理程序 - 避免在渲染中绑定
  • 它没有帮助我。
  • 错误仍然存​​在?尝试直接渲染数据,没有InfiniteScroll
  • 简而言之,我删除了fetchRegistersOnScroll()的page++,然后一切都开始工作了,或者更确切地说,所有的寄存器都显示出来了。但是现在的问题是有的显示两次,如何解决?
  • 您正在从父级(第 0 页?)传递一些数据 - 需要吗?您可以从 componentDidMount 调用 first fetch,在一个组件中保持状态(当前页面,下一次获取的起始索引)。检查请求/网络选项卡中的参数 - 获取可能具有重叠范围。

标签: reactjs react-redux


【解决方案1】:

在您的 cmets 之后,您已经解决了数据从页面中消失的问题,但现在您有数据重复问题,可以通过修改 fetchRegisterOnScroll 函数来解决。由于 InfiniteScroll 可能会一次又一次地调用 loadMore 函数,所以我们需要检查它是否被调用。

下面是可能有帮助的代码 sn-p。

fetchRegistersOnScroll() {
    if (this.state.isFetching) return;         // checking request has already been called or not.
    const { actions, current, dispatch } = this.props

    this.setState({ isFetching: true });
    actions.fetchRegisters(current, page)
      .then(res => {
        dispatch({ type: 'REGISTER/SCROLL', payload: res.data });

        if (res.data.items.length < 20)
          this.setState({ hasMoreItems: false, isFetching: false })
        else this.setState({ isFetching: true })
      })
  }

我认为这会有所帮助。谢谢!!

【讨论】:

  • 我喜欢你的回答,但是很遗憾,它并没有解决我的问题,我继续收到同样的错误。
  • 你能分享一下你调度的两个动作的代码吗?基本上是 REGISTER/FETCH 动作和 REGISTER/SCROLL 动作?
猜你喜欢
  • 2020-04-16
  • 2016-10-03
  • 2013-04-12
  • 2021-04-05
  • 1970-01-01
  • 1970-01-01
  • 2015-03-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多