将 Redux 与 React Hooks 一起使用时的无限 useEffect 循环

Jon*_*ell 2 reactjs redux react-hooks

我正在重构一些代码并将我的类组件转换为函数组件,以此作为学习如何使用 Hooks 和 Effects 的一种方式。我的代码使用 Redux 进行状态管理,使用 axios 进行数据库请求,使用 Thunk 作为中间件处理异步性。我在一个组件中遇到问题,该组件执行 get 请求以检索过去的客户列表componentDidMount。无论我尝试什么,该useEffect函数都会进入无限循环并继续请求客户列表。

有问题的组件CustomersTable从数据库中获取客户列表并将其显示在表格中。该组件由容器组件包装,该组件使用 Redux 的连接将检索到的客户列表CustomersTable作为道具传递给。

useEffect(() => {
    loadCustomers(currentPage, itemsPerPage, sortProp, (ascending ? 'asc' : 'desc'), {});
  }, []);
Run Code Online (Sandbox Code Playgroud)

loadCustomers 是一个 Redux 操作,它使用 axios 来获取客户列表。currentPage、itemsPerPage、sortProp 和升序是初始化为“组件安装”上特定值的状态变量

我希望这是因为我使用空数组,它只会运行一次。相反,它会持续运行。我不明白为什么会这样。我最好的猜测是,当 redux 获取列表时,它返回一个新的 state 对象,因此 props 每次都会改变,然后触发重新渲染,然后获取一个新列表。我使用这个错误是因为 Redux 不打算与这样的钩子一起使用吗?

我最终通过添加以下内容来完成这项工作:

useEffect(() => {
    if (!list.length) {
      loadCustomers(currentPage, itemsPerPage, sortProp, (ascending ? 'asc' : 'desc'), {});
    }
  }, []);
Run Code Online (Sandbox Code Playgroud)

我不确定这是我真正想要的行为。如果客户列表确实为 0,则代码将继续获取列表。如果列表真的是空的,那么我希望它只获取一次然后停止。编辑:事实证明这绝对行不通。它适用于初始加载,但会破坏任何删除或编辑的代码。

好的,在此处提供更多上下文。包装客户表的容器组件是:

import { connect } from 'react-redux';
import loadCustomers from './actions/customersActions';
import { deleteCustomer } from './actions/customerActions';
import CustomersTable from './CustomersTableHooks';

function mapStateToProps(state) {
  return {
    customers: state.customers,
    customer: state.customer
  };
}

export default connect(mapStateToProps, { loadCustomers, deleteCustomer })(CustomersTable);
Run Code Online (Sandbox Code Playgroud)

动作,loadCustomers 是:

export default function loadCustomers(page = 1, itemsPerPage = 50, sortProp = 'id', sortOrder = 'asc', search = {}) {
  return (dispatch) => {
    dispatch(loadCustomersBegin());
    return loadCustomersApi(page, itemsPerPage, sortProp, sortOrder, search)
      .then(data => dispatch(loadCustomersSuccess(data)))
      .catch(() => dispatch(loadCustomersFailure()));
  };
}
Run Code Online (Sandbox Code Playgroud)

为客户提供的减速机是:

export default function customersReducer(state = initialState, action) {
  switch (action.type) {
    case types.LOAD_CUSTOMERS_BEGIN:
      return Object.assign({}, state, { isLoading: true, list: [], totalItems: 0 });
    case types.LOAD_CUSTOMERS_SUCCESS:
      return Object.assign({}, state, { isLoading: false, list: action.customers || [], totalItems: action.totalItems });
    case types.LOAD_CUSTOMERS_FAILURE:
      return Object.assign({}, state, { isLoading: false, list: [], totalItems: 0 });
    default:
      return state;
  }
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,我无法发布 CustomerTable 本身的大部分内容,因为事物的命名方式会告诉您我为哪家公司工作。

dee*_* zg 7

因此,如果我正确理解您的代码,那么您将loadCustomers在子组件中分派操作,useEffect但您会在 parents 中读取实际数据mapStateToProps

当然,这会创建无限循环:

  1. 父级customers从商店读取(或从商店中读取任何内容,就此而言)
  2. 呈现孩子
  3. 孩子customers进来useEffect
  4. 父属性更改并导致重新渲染
  5. 整个故事永远持续下去

故事的寓意:不要dispatch来自展示组件。或者,换句话说,dispatch来自您从 store 读取相同属性的同一组件的操作。