[英]conditional async actions in componentDidMount lifecycle method keeps going on a loop
我正在使用连接到API的redux和sagas开发一个React应用程序。
有一个表单组件具有两个下拉字段:“程序”和“联系人”字段。 表单设计的工作方式是,当用户选择程序时,表单使用programId来获取已为该程序注册的所有联系人。 然后,将这些联系人填充为“联系人”下拉字段的选项。 这有效,我已经使用componentWillReceiveProps实现了它,如下所示:-
componentWillReceiveProps(nextProps) {
if (nextProps.programId !== this.props.programId) {
this.props.fetchProgramContacts(nextProps.programId);
}
}
现在,我正在尝试具有一项附加功能,当从程序的配置文件页面访问该表单时,该表单会使用programId自动填充该表单。 在这种情况下,由于即使在组件安装之前,programId仍已预加载到formData中,所以不会触发componentWillReceiveProps,因为prop中没有任何更改。 因此,我决定在componentDidMount生命周期方法中获取programContacts,如下所示:
componentDidMount() {
if (this.props.programId !== '' && !this.props.programContactData.length) {
this.props.fetchProgramContacts(this.props.programId);
}
}
逻辑是仅当programId不为空且programContacts为空时才必须进行获取请求。 但这是一个无休止的获取循环。
我发现if语句会一遍又一遍地执行,因为if语句主体中的表达式甚至在之前的获取请求与结果一起返回之前由componentDidMount再次执行。 而且,由于条件之一就是检查结果数组的长度是否为非空,因此if语句返回true,因此循环继续进行而没有让先前的请求完成。
我不明白的是为什么必须重复执行if语句。 一旦if语句执行一次,它不应该退出生命周期方法吗?
我知道也许可以使用某种超时方法来使它正常工作,但这对我来说还不够强大。
是否有最佳实践来实现这一目标?
另外,如果componentDidMount方法中有条件,是否有不建议使用的建议?
在React生命周期中, componentDidMount()
仅触发一次。
确保调用是从componentDidMount
而不是componentWillReceiveProps
。
如果调用确实来自componentDidMount
,则意味着每次都会重新创建组件。 可以通过在组件的constructor
中添加console.log
进行检查。
无论如何,您应该更喜欢使用redux的isFetching
和didInvalidate
来处理数据获取/重新获取。
您可以在另一个问题中看到我的详细解答之一: 组件中的React-Redux状态与商店中的状态不同
如果我关注您的用例,则可以在下面看到isFetching
和didInvalidate
概念的应用程序。
1.组成
看一下动作和isFetching
,但是redux的窍门是使用isFetching
和didInvalidate
道具。
当您要获取数据时,仅有的两个问题是:
您可以在下面看到,无论何时选择程序,都将使获取的数据无效 ,以便使用新的programId作为过滤器再次获取。
注意:当然,您应该使用redux
connect
来将动作和redux
传递给组件!
MainView.js
class MainView extends React.Component {
return (
<div>
<ProgramDropdown />
<ContactDropdown />
</div>
);
}
ProgramDropdown.js
class ProgramDropdown extends React.Component {
componentDidMount() {
if (this.props.programs.didInvalidate && !this.props.programs.isFetching) {
this.props.actions.readPrograms();
}
}
render() {
const {
isFetching,
didInvalidate,
data,
} = this.props;
if (isFetching || (didInvalidate && !isFetching)) {
return <select />
}
return (
<select>
{data.map(entry => (
<option onClick={() => this.props.actions.setProgram(entry.id)}>
{entry.value}
</option>
))}
</select>
);
}
}
ContactDropdown.js
class ContactDropdown extends React.Component {
componentDidMount() {
if (this.props.programs.selectedProgram &&
this.props.contacts.didInvalidate && !this.props.contacts.isFetching) {
this.props.actions.readContacts(this.props.programs.selectedProgram);
}
}
componentWillReceiveProps(nextProps) {
if (nextProps.programs.selectedProgram &&
nextProps.contacts.didInvalidate && !nextProps.contacts.isFetching) {
nextProps.actions.readContacts(nextProps.programs.selectedProgram);
}
}
render() {
const {
isFetching,
didInvalidate,
data,
} = this.props;
if (isFetching || (didInvalidate && !isFetching)) {
return <select />
}
return (
<select>
{data.map(entry => (
<option onClick={() => this.props.actions.setContact(entry.id)}>
{entry.value}
</option>
))}
</select>
);
}
}
2.接触动作
我将仅关注接触动作,因为程序之一几乎相同。
export function readContacts(programId) {
return (dispatch, state) => {
dispatch({ type: 'READ_CONTACTS' });
fetch({ }) // Insert programId in your parameter
.then((response) => dispatch(setContacts(response.data)))
.catch((error) => dispatch(addContactError(error)));
};
}
export function selectContact(id) {
return {
type: 'SELECT_CONTACT',
id,
};
}
export function setContacts(data) {
return {
type: 'SET_CONTACTS',
data,
};
}
export function addContactError(error) {
return {
type: 'ADD_CONTACT_ERROR',
error,
};
}
3.联系异径管
import { combineReducers } from 'redux';
export default combineReducers({
didInvalidate,
isFetching,
data,
selectedItem,
errors,
});
function didInvalidate(state = true, action) {
switch (action.type) {
case 'SET_PROGRAM': // !!! THIS IS THE TRICK WHEN YOU SELECT ANOTHER PROGRAM, YOU INVALIDATE THE FETCHED DATA !!!
case 'INVALIDATE_CONTACT':
return true;
case 'SET_CONTACTS':
return false;
default:
return state;
}
}
function isFetching(state = false, action) {
switch (action.type) {
case 'READ_CONTACTS':
return true;
case 'SET_CONTACTS':
return false;
default:
return state;
}
}
function data(state = {}, action) {
switch (action.type) {
case 'SET_CONTACTS':
return action.data;
default:
return state;
}
}
function selectedItem(state = null, action) {
switch (action.type) {
case 'SELECT_CONTACT':
return action.id;
case 'READ_CONTACTS':
case 'SET_CONTACTS':
return null;
default:
return state;
}
}
function errors(state = [], action) {
switch (action.type) {
case 'ADD_CONTACT_ERROR':
return [
...state,
action.error,
];
case 'SET_CONTACTS':
return state.length > 0 ? [] : state;
default:
return state;
}
}
希望能帮助到你。
实际的问题出在componentWillReceiveProps方法本身中,在这里创建了无限循环。 您正在检查当前和下一个programId是否不匹配,然后触发将使当前和下一个programId再次不匹配的操作。 使用给定的fetchProgramContacts操作,您将以某种方式更改programId。 检查您的减速器。
解决此问题的方法之一是在减速器中进行reqFinished(true / false),然后您应该执行以下操作:
componentWillReceiveProps(nextProps){
if(nextProps.reqFinished){
this.props.fetchProgramContacts(nextProps.programId);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.