繁体   English   中英

componentDidMount生命周期方法中的条件异步动作不断循环

[英]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的isFetchingdidInvalidate来处理数据获取/重新获取。

您可以在另一个问题中看到我的详细解答之一: 组件中的React-Redux状态与商店中的状态不同


如果我关注您的用例,则可以在下面看到isFetchingdidInvalidate概念的应用程序。

1.组成

看一下动作和isFetching ,但是redux的窍门是使用isFetchingdidInvalidate道具。

当您要获取数据时,仅有的两个问题是:

  1. 我的数据仍然有效吗?
  2. 我当前正在获取数据吗?

您可以在下面看到,无论何时选择程序,都将使获取的数据无效 ,以便使用新的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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM