如何使用 redux 在首次渲染期间指示异步操作的 "loading" 状态

How to indicate "loading" state for an async action during first render using redux

假设我有一个如下所示的 redux 模块:

import fetch from 'isomorphic-fetch';

// CONSTANTS
const LOAD = 'LOAD';
const LOAD_SUCCESS = 'LOAD_SUCCESS';

// REDUCER
const initialState = { loading: false, data: [] };

export default function reducer(state = initialState, action) {
  switch (action.type) {
    case LOAD: return { ...state, loading: true };
    case LOAD_SUCCESS: return { ...state, loading: false, data: action.data };
    default: return state;
  }
}

// ACTION CREATORS
function requestLocations() {
  return { type: LOAD };
}

function receiveLocations(json) {
  return { type: LOAD_SUCCESS, data: json };
}

export function fetchLocations() {
  return function (dispatch) {
    dispatch(requestLocations());

    return fetch('http://myurl')
      .then(response => response.json())
      .then(json => dispatch(receiveLocations(json)));
  };
}

如果我在 componentWillMount 中进行异步调用,我会在第一次渲染时遇到 loading 状态。想象一下我的组件看起来像这样(为简洁起见进行了简化):

export default class LocationContainer extends React.Component {
  componentWillMount() {
    fetchLocations(); // already bound and injected via connect.
  }

  render() {
    const { loading, data } = this.props; // injected via connect from reducer above.
    if (loading) {
      return <Spinner />;
    } else {
      return <LocationExplorer locations={ data } />;
    }
  }
}

我 运行 遇到的问题是,在 LocationContainer 的第一次渲染中,loading 是假的,并且 data 还没有被提取。在 componentWillMount 中,LOAD 动作被触发,loading 的道具更改被设置为 true 排队等待在后续渲染中发生。与此同时,在我的第一次渲染中,当我真正想要 Spinner 而不是 LocationExplorer 时渲染了 LocationExplorer,因为 loading 仍然是错误的。我想知道你如何在不设置 firstRender = true 状态变量 hack 的情况下处理这个问题。

不要在 componentWillMount 中提出请求。相反,请在安装组件之前执行此操作。例如

store.dispatch(fetchLocations());
ReactDOM.render(<App store={store} />, mountpoint);

一个选项可以使用 data 初始状态扩展 loading 条件:

const initialState = { loading: false, data: [] };

当您 loading 而您的 data 为空时,这意味着您正处于等待新数据到来的确切状态:

if (loading && data.length === 0) {
  return <Spinner />;
} else {

此外,我通常将异步调用放在 componentDidMount 而不是 componentWillMount

除了公认的答案之外,我们一直在使用并取得巨大成功的另一种解决方案是向 reducer 添加一个 complete 布尔标志,如果响应在某个时候返回,我们将其标记为真。如果响应实际返回一个空数组,这会使事情变得更加明确。 complete 标志让我知道响应来自服务器,而不仅仅是我的初始状态。

if(loading && !complete) {
  return <Spinner />;
} else {
  return <Component data={data} />;
}

我想我会添加这个以防它对其他人有帮助。