componentDidMount生命周期方法中的条件异步操作继续循环

时间:2017-10-27 11:46:01

标签: javascript reactjs redux redux-saga react-lifecycle

我正在研究使用连接到API的redux和sagas的反应应用程序。

表单组件有两个下拉字段: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为空时才必须进行获取请求。但这是一个无休止的获取循环。

我发现if语句被反复执行,因为if语句的主体中的表达式甚至在前一个获取请求与结果一起返回之前由componentDidMount再次执行。并且因为其中一个条件是检查结果数组的长度是否为非空,if语句返回true,因此循环继续运行而不让先前的请求完成。

我不明白为什么if语句必须重复执行。一旦if语句执行一次,它不应该退出生命周期方法吗?

我知道也许可以使用某种超时方法来实现这一点,但这对我来说并不是一种足够强大的技术。

有最好的做法可以实现这个目标吗?

另外,有没有建议不使用componentDidMount方法中的条件?

2 个答案:

答案 0 :(得分:2)

在React生命周期中,componentDidMount()仅触发一次。

确保通过componentDidMount而非componentWillReceiveProps进行通话。

如果呼叫来自componentDidMount,则表示每次都重新创建组件。 可以通过在组件的console.log中添加constructor来检查。

在任何情况下,您都应该使用redux的isFetchingdidInvalidate来处理数据提取/重新获取。

您可以在另一个问题中查看我的详细解答:React-Redux state in the component differs from the state in the store

如果我专注于您的用例,您可以在下面看到isFetchingdidInvalidate概念的应用。

<强> 1。部件

看看动作和减速器,但使用redux的技巧是使用isFetchingdidInvalidate道具。

当您想要获取数据时,唯一的两个问题是:

  1. 我的数据是否仍然有效?
  2. 我目前是否正在提取数据?
  3. 您可以在下面看到,每当您选择一个程序时,您将使取出的数据无效,以便使用新的programId作为过滤器再次获取。

    注意:您应该使用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;
      }
    }
    

    希望它有所帮助。

答案 1 :(得分:0)

实际问题在于componentWillReceiveProps方法本身,这里创建了无限循环。 您正在检查当前和下一个programId是否不匹配,然后触发一个动作,使当前和下一个programId不再匹配。使用给定的动作fetchProgramContacts,你以某种方式改变programId。检查减压器。

解决方法之一是在reducer中使用reqFinished(true / false),然后你应该这样做:

componentWillReceiveProps(nextProps){
  if(nextProps.reqFinished){
    this.props.fetchProgramContacts(nextProps.programId);
  }
}