【发布时间】:2019-04-05 15:56:10
【问题描述】:
我正在重构一些代码并将我的类组件转换为函数组件,以此作为学习如何使用 Hooks 和 Effects 的一种方式。我的代码使用 Redux 进行状态管理,使用 axios 进行数据库请求,使用 Thunk 作为中间件来处理异步性。我在一个组件中遇到问题,该组件执行获取请求以检索以前为componentDidMount 的客户列表。无论我尝试什么,useEffect 函数都会进入无限循环并继续请求客户列表。
有问题的组件CustomersTable 从数据库中获取客户列表并将其显示在表格中。该组件由一个容器组件包装,该组件使用 Redux 的 connect 将检索到的客户列表作为道具传递给 CustomersTable。
useEffect(() => {
loadCustomers(currentPage, itemsPerPage, sortProp, (ascending ? 'asc' : 'desc'), {});
}, []);
loadCustomers 是一个 Redux 操作,它使用 axios 来获取客户列表。 currentPage、itemsPerPage、sortProp 和ascending 是状态变量,在“组件挂载”上初始化为特定值
我希望因为我使用空数组,它只会运行一次。相反,它连续运行。我无法弄清楚为什么会这样。我最好的猜测是,当 redux 获取列表时,它会返回一个新的状态对象,因此 props 每次都会更改,然后触发重新渲染,然后获取一个新列表。我是不是用错了,因为 Redux 不应该与这样的钩子一起使用?
我最终通过添加以下内容来完成这项工作:
useEffect(() => {
if (!list.length) {
loadCustomers(currentPage, itemsPerPage, sortProp, (ascending ? 'asc' : 'desc'), {});
}
}, []);
我不确定这是我真正想要的行为。如果客户列表确实为 0,那么代码将继续获取该列表。如果列表真的是空的,那么我希望它只获取一次然后停止。编辑:事实证明这绝对行不通。它适用于初始加载,但会破坏任何删除或编辑的代码。
好的,这里提供更多上下文。包装了 CustomersTable 的容器组件是:
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;
}
}
很遗憾,我不能发布太多 CustomersTable 本身的内容,因为这些内容的命名方式可以告诉您我在为哪家公司工作。
【问题讨论】:
-
上面的代码似乎是正确的。您可以发布创建可重现演示的整个组件
-
这是工作代码,所以很遗憾我不能发布整个组件,也不能发布演示。
-
没有必要按原样发布您的代码,我们只需要一个类似于您的虚拟设置来重现问题
-
嗯,我必须发布很多代码来显示类似的设置,包括所有 Redux 代码。我想我是想问一个更理论的问题。由于我没有将客户列表加载到状态中,而是将其加载到 Redux 状态然后将其作为道具传递,我是否会导致无限循环,因为这会注册为更改道具并因此重新渲染?
-
你是对的,但是某些 useEffect 是通过第二个参数空数组传递的,它告诉 react 它应该只运行一次,所以这里可能有其他代码在工作中导致问题,所以我们需要看看更多上下文。
标签: reactjs redux react-hooks