我正在重构一些代码,并将我的类组件转换为功能组件,以此作为学习如何使用挂钩和效果的方式。我的代码使用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本身,因为事物的命名方式可以告诉您我正在为哪个公司工作。
答案 0 :(得分:1)
在每个渲染上,您都会得到新的客户对象,因为mapStateToProps如果做浅相等。您可以使用备注选择器来吸引客户,并且在不需要时不会重新呈现。
import { createSelectorCreator, defaultMemoize } from 'reselect';
const createDeepEqualSelector = createSelectorCreator(defaultMemoize, deepEqual);
const customerSelector = createDeepEqualSelector(
[state => state.customerReducer.customers],
customers => customers,
);
答案 1 :(得分:1)
我不同意此处投票最多的答案。
<块引用>当依赖项的第二个参数是空数组时,重新渲染不会调用 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
中读取了实际数据。
这当然会创建无限循环,如下所示:
customers
(或从商店中读取任何内容)customers
中获取useEffect
故事的寓意:不要dispatch
来自陈述性内容。或者换句话说,dispatch
来自您从商店读取相同属性的相同组件中的操作。