我正在使用连接到 API 的 redux 和 sagas 开发一个 React 应用。
有一个表单组件有两个下拉字段:一个 Program 和一个 Contact 字段。该表单设计的工作方式是,当用户选择一个程序时,该表单使用 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为空时,才必须进行fetch请求。但这会无限循环获取。
我发现 if 语句被反复执行,因为 if 语句主体中的表达式甚至在前一个获取请求返回结果之前就被 componentDidMount 再次执行。并且因为其中一个条件是检查结果数组的长度是否为非空,所以 if 语句返回 true,因此循环继续进行,而不会让先前的请求完成。
我不明白的是为什么if语句必须重复执行。难道if语句执行一次就退出生命周期方法吗?
我知道也许可以使用某种超时方法来让它工作,但这不是我可以依赖的足够强大的技术。
是否有实现此目的的最佳实践?
此外,是否有任何建议不要在 componentDidMount 方法中使用 if 条件?
最佳答案
在 React 生命周期中,componentDidMount()
只会被触发一次。
确保调用是从 componentDidMount
而不是 componentWillReceiveProps
进行的。
如果调用确实来自 componentDidMount
,则意味着您的组件每次都会重新创建。
可以通过在组件的 constructor
中添加 console.log
来检查它。
无论如何,您应该更喜欢使用 redux 的 isFetching
和 didInvalidate
来处理数据获取/重新获取。
您可以在另一个问题中看到我对其工作原理的详细回答之一:React-Redux state in the component differs from the state in the store
如果我专注于您的用例,您可以在下面看到 isFetching
和 didInvalidate
概念的应用。
<强>1。组件
看看 Action 和缩减器,但 redux 的技巧是使用 isFetching
和 didInvalidate
Prop 。
当您想要获取数据时,唯一的两个问题是:
- 我的数据是否仍然有效?
- 我目前正在获取数据吗?
您可以在下面看到,每当您选择一个程序时,您都会使获取的数据,以便使用新的 programId 作为过滤器再次获取。
注意:当然,您应该使用 redux
的 connect
将操作和 reducer 传递给您的组件!
主视图.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。联系 reducer
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;
}
}
希望对您有所帮助。
关于javascript - componentDidMount 生命周期方法中的条件异步操作不断循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46974280/