将Redux与React Hooks一起使用时会出现无限useEffect循环

时间:2019-04-05 15:56:11

标签: reactjs redux

我正在重构一些代码,并将我的类组件转换为功能组件,以此作为学习如何使用挂钩和效果的方式。我的代码使用Redux进行状态管理,使用axios进行数据库请求,并使用Thunk作为中间件来处理异步性。我在一个组件中遇到一个问题,该组件发出get请求以检索以前是componentDidMount的客户列表。无论我做什么,useEffect函数都会陷入无限循环,并继续请求客户列表。

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

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

loadCustomers是Redux操作,它使用axios来获取客户列表。 currentPage,itemsPerPage,sortProp和ascending是状态变量,已在“组件安装”中初始化为特定值

我希望这是因为我使用了空数组,因此只能运行一次。而是连续运行。我不知道为什么会这样。我最好的猜测是,当redux获取列表时,它将返回一个新的状态对象,因此道具每次都会更改,然后触发重新渲染,然后再获取新列表。我使用这个错误是因为Redux不是要与这样的钩子一起使用吗?

我最终通过添加以下内容使此工作正常进行:

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

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

确定,在此提供更多上下文。包装CustomerTable的容器组件是:

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

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

客户的减速器是:

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

不幸的是,我无法发布很多CustomerTable本身,因为事物的命名方式可以告诉您我正在为哪个公司工作。

3 个答案:

答案 0 :(得分:1)

在每个渲染上,您都会得到新的客户对象,因为mapStateToProps如果做浅相等。您可以使用备注选择器来吸引客户,并且在不需要时不会重新呈现。

import { createSelectorCreator, defaultMemoize } from 'reselect';

const createDeepEqualSelector = createSelectorCreator(defaultMemoize, deepEqual);

const customerSelector = createDeepEqualSelector(
 [state => state.customerReducer.customers],
 customers => customers,
);

答案 1 :(得分:1)

我不同意此处投票最多的答案。

<块引用>
  1. 父属性更改并导致重新渲染
  2. 整个故事永远持续下去

当依赖项的第二个参数是空数组时,重新渲染不会调用 Dim MyTableArray As Range, MyEmpID As String Set MyTableArray = Sheets("CompressorData").Range("A:D") Me.txtName.Value = WorksheetFunction.VLookup(Me.ComboBox1, MyTableArray, 2, 0) Me.TextBox3.Value = WorksheetFunction.VLookup(Me.ComboBox1, MyTableArray, 1, 0) Me.TextBox1.Value = WorksheetFunction.VLookup(Me.ComboBox1, MyTableArray, 4, 0) End Sub 的函数参数。这个函数只会第一次被调用,类似于生命周期方法useEffect。似乎创建此主题是因为没有发生正确预期的行为。

似乎正在发生的事情是组件正在卸载然后再次安装。我遇到了这个问题,它把我带到了这里。不幸的是,如果没有组件代码,我们只能猜测实际原因。我以为它与 componentDidMount react-redux 有关,但事实证明并非如此。在我的情况下,问题是有人在组件中定义了组件,并且在每次渲染时重新定义/重新创建组件。这似乎是一个类似的问题。我最终将该嵌套定义包装在 connect 中。

答案 2 :(得分:0)

因此,如果我正确理解您的代码,则说明您正在loadCustomers中的子组件中分派useEffect操作,但是您在父项mapStateToProps中读取了实际数据。

这当然会创建无限循环,如下所示:

  1. 父母从商店中读取customers(或从商店中读取任何内容)
  2. 让孩子们
  3. 子级在customers中获取useEffect
  4. 父级属性更改并导致重新呈现
  5. 整个故事永远持续下去

故事的寓意:不要dispatch来自陈述性内容。或者换句话说,dispatch来自您从商店读取相同属性的相同组件中的操作。