javascript - 如何在获取数据时在React Redux应用程序中显示加载指示器?

我是React / Redux的新手。 我在Redux应用程序中使用fetch api中间件来处理API。 它的[https://github.com/agraboso/redux-api-middleware。]我认为这是处理异步api操作的好方法。 但我发现有些案例无法由我自己解决。

正如主页[https://github.com/agraboso/redux-api-middleware#lifecycle]所说,fetch API生命周期开始于调度CALL_API操作以调度FSA操作结束。

所以我的第一个案例是在获取API时显示/隐藏预加载器。 中间件将在开始时发送FSA操作,并在最后发送FSA操作。 这两个动作都是由减速器接收的,减速器应该只进行一些正常的数据处理。 没有UI操作,没有更多操作。 也许我应该将处理状态保存在状态中,然后在存储更新时呈现它们。

但是怎么做呢? 一个反应组件流过整个页面? 从其他操作更新商店会发生什么? 我的意思是他们更像是事件而非国家!

即使是更糟糕的情况,当我必须在redux / react应用程序中使用本机确认对话框或警报对话框时,我该怎么办? 它们应该放在哪里,行动还是减少?

最好的祝愿! 希望回复。

8个解决方案
145 votes

我的意思是他们更像是事件而非国家!

我不会这么说。 我认为加载指标是UI的一个很好的例子,很容易被描述为状态的函数:在这种情况下,是一个布尔变量。 虽然这个答案是正确的,但我想提供一些代码来配合它。

在Redux repo中的isFetching示例中,reducer更新了一个名为isFetching的字段:

case REQUEST_POSTS:
  return Object.assign({}, state, {
    isFetching: true,
    didInvalidate: false
  })
case RECEIVE_POSTS:
  return Object.assign({}, state, {
    isFetching: false,
    didInvalidate: false,
    items: action.posts,
    lastUpdated: action.receivedAt

该组件使用React Redux的isFetching订阅商店的状态,并返回isFetching作为render()返回值的一部分,因此它在连接组件的props中可用:

function mapStateToProps(state) {
  const { selectedReddit, postsByReddit } = state
  const {
    isFetching,
    lastUpdated,
    items: posts
  } = postsByReddit[selectedReddit] || {
    isFetching: true,
    items: []
  }

  return {
    selectedReddit,
    posts,
    isFetching,
    lastUpdated
  }
}

最后,该组件在render()函数中使用isFetching prop来呈现“正在加载...”标签(可以想象它是一个微调器):

{isEmpty
  ? (isFetching ? <h2>Loading...</h2> : <h2>Empty.</h2>)
  : <div style={{ opacity: isFetching ? 0.5 : 1 }}>
      <Posts posts={posts} />
    </div>
}

即使是更糟糕的情况,当我必须在redux / react应用程序中使用本机确认对话框或警报对话框时,我该怎么办? 它们应该放在哪里,行动还是减少?

任何副作用(并显示对话框肯定是副作用)不属于reducer。 将减速器视为被动的“国家建设者”。 他们并没有真正“做”事情。

如果您希望显示警报,请在发送操作之前从组件执行此操作,或者从操作创建者执行此操作。 在调度动作时,为了响应它而执行副作用为时已晚。

对于每个规则,都有一个例外。 有时你的副作用逻辑是如此复杂,你实际上想要将它们耦合到特定的动作类型或特定的减速器。 在这种情况下,请查看Redux Saga和Redux Loop等高级项目。 只有当你对香草Redux感到舒服并且有一个真正的分散副作用问题时才能做到这一点,你想让它更易于管理。

Dan Abramov answered 2019-08-13T13:35:09Z
19 votes

伟大的回答丹阿布拉莫夫!只是想补充说我在我的一个应用程序中做了或多或少的操作(将isFetching保留为布尔值)并最终必须使其成为一个整数(最终读取为未完成请求的数量)以支持多个同时 要求。

用布尔值:

请求1开始 - &gt; 旋转器 - &gt; 请求2开始 - &gt; 请求1结束 - &gt; 旋转器 - &gt; 请求2结束

带整数:

请求1开始 - &gt; 旋转器 - &gt; 请求2开始 - &gt; 请求1结束 - &gt; 请求2结束 - &gt; 旋转器

case REQUEST_POSTS:
  return Object.assign({}, state, {
    isFetching: state.isFetching + 1,
    didInvalidate: false
  })
case RECEIVE_POSTS:
  return Object.assign({}, state, {
    isFetching: state.isFetching - 1,
    didInvalidate: false,
    items: action.posts,
    lastUpdated: action.receivedAt
Nuno Campos answered 2019-08-13T13:36:05Z
12 votes

我想补充一些东西。 真实世界示例在商店中使用字段fetching来表示何时获取项目集合。 任何集合都推广到[entity, isFetching]减速器,可以连接到组件以跟踪状态并显示集合是否正在加载。

我碰巧想要获取一个不适合分页模式的特定实体的细节。 我希望有一个状态来表示是否从服务器获取了详细信息,但我也不想为此设置一个reducer。

为了解决这个问题,我添加了另一个名为fetching的通用减速器。它的工作方式与分页减速器类似,它的职责就是观察一组动作并用对[entity, isFetching]生成新状态。这样就可以减速器connect 到任何组件并知道应用程序当前是否正在获取数据,不仅仅是针对集合而是针对特定实体。

javivelasco answered 2019-08-13T13:36:53Z
5 votes

直到现在我才发现这个问题,但由于没有接受任何答案,我会戴上帽子。 我为这个工作写了一个工具:react-loader-factory。 它比阿布拉莫夫的解决方案稍微复杂一点,但更加模块化和方便,因为我写完之后并不想思考。

有四大块:

  • 工厂模式:这允许您快速调用相同的功能来设置哪些状态意味着&#34;加载&#34; 对于您的组件,以及要分派的操作。 (这假定组件负责启动它等待的操作。)ACTION_SUCCESS
  • 包装器:工厂生产的组件是一个&#34;更高阶的组件&#34; (就像ACTION_SUCCESS在Redux中返回的那样),这样你就可以把它固定在你现有的东西上。ACTION_REQUEST
  • Action / Reducer交互:包装器检查它所插入的reducer是否包含告诉它不要传递给需要数据的组件的关键字。 由包装器调度的操作预计会产生关联的关键字(例如,redux-api-middleware调度方式为ACTION_SUCCESSACTION_REQUEST)。 (当然,您可以在其他地方发送操作,只需要从包装器进行监控。)
  • Throbber:您希望在组件所依赖的数据时显示的组件尚未准备就绪。 我在那里添加了一个小div,所以你可以测试它而不必进行操作。

该模块本身独立于redux-api-middleware,但这是我使用它的原因,所以这里有一些来自README的示例代码:

包含Loader的组件:

import React from 'react';
import { myAsyncAction } from '../actions';
import loaderFactory from 'react-loader-factory';
import ChildComponent from './ChildComponent';

const actionsList = [myAsyncAction()];
const monitoredStates = ['ASYNC_REQUEST'];
const loaderWrapper = loaderFactory(actionsList, monitoredStates);

const LoadingChild = loaderWrapper(ChildComponent);

const containingComponent = props => {
  // Do whatever you need to do with your usual containing component 

  const childProps = { someProps: 'props' };

  return <LoadingChild { ...childProps } />;
}

要监控的Loader的减速器(尽管您可以根据需要以不同的方式连接它):

export function activeRequests(state = [], action) {
  const newState = state.slice();

  // regex that tests for an API action string ending with _REQUEST 
  const reqReg = new RegExp(/^[A-Z]+\_REQUEST$/g);
  // regex that tests for a API action string ending with _SUCCESS 
  const sucReg = new RegExp(/^[A-Z]+\_SUCCESS$/g);

  // if a _REQUEST comes in, add it to the activeRequests list 
  if (reqReg.test(action.type)) {
    newState.push(action.type);
  }

  // if a _SUCCESS comes in, delete its corresponding _REQUEST 
  if (sucReg.test(action.type)) {
    const reqType = action.type.split('_')[0].concat('_REQUEST');
    const deleteInd = state.indexOf(reqType);

    if (deleteInd !== -1) {
      newState.splice(deleteInd, 1);
    }
  }

  return newState;
}

我希望在不久的将来,我会在模块中添加超时和错误等内容,但模式不会有太大差异。


你问题的简短回答是:

  1. 将渲染与渲染代码联系起来 - 使用您需要使用上面显示的数据渲染的组件周围的包装器。
  2. 添加一个缩减器,使您可能关心的应用程序的状态容易消化,因此您不必过于考虑正在发生的事情。
  3. 事件和国家并没有真正的不同。
  4. 你的其他直觉对我来说似乎是正确的。
bright-star answered 2019-08-13T13:39:13Z
3 votes

您可以使用React Redux的alert或低级别的confirm方法向商店添加更改侦听器。 您应该在商店中有加载指示器,然后商店更改处理程序可以检查并更新组件状态。 然后,组件根据状态在需要时呈现预加载器。

alertconfirm应该不成问题。 它们是阻塞的,警报甚至不会从用户那里获得任何输入。 使用confirm,如果用户选择应影响组件呈现,则可以根据用户单击的内容设置状态。 如果没有,您可以将选项存储为组件成员变量以供以后使用。

Miloš Rašić answered 2019-08-13T13:39:52Z
3 votes

我们的应用程序中有三种类型的通知,所有通知都设计为方面:

  1. 装载指示器(基于道具的模态或非模态)
  2. 错误弹出(模态)
  3. 通知零食栏(非模态,自闭)

所有这三个都位于我们的应用程序(主要)的顶层,并通过Redux连接,如下面的代码片段所示。 这些道具控制其相应方面的显示。

我设计了一个处理所有API调用的代理,因此所有isFetching和(api)错误都是由我在代理中导入的actionCreators调解的。 (顺便说一下,我也使用webpack为dev注入一个支持服务的模拟,这样我们就可以在没有服务器依赖的情况下工作。)

应用程序中需要提供任何类型通知的任何其他位置只需导入相应的操作。 Snackbar&amp; 错误具有要显示的消息的参数。

@connect(
// map state to props
state => ({
    isFetching      :state.main.get('isFetching'),   // ProgressIndicator
    notification    :state.main.get('notification'), // Snackbar
    error           :state.main.get('error')         // ErrorPopup
}),
// mapDispatchToProps
(dispatch) => { return {
    actions: bindActionCreators(actionCreators, dispatch)
}}

)export default class Main扩展React.Component {

Dreculah answered 2019-08-13T13:41:17Z
3 votes

我是唯一一个认为加载指标不属于Redux商店的人吗? 我的意思是,我不认为它本身就是应用程序的一部分。

现在,我使用Angular2,我所做的就是我有一个&#34; Loading&#34; 通过RxJS BehaviourSubjects公开不同加载指标的服务..我猜机制是一样的,我只是不把信息存储在Redux中。

LoadingService的用户只是订阅他们想要收听的事件。

每当事情需要改变时,我的Redux动作创建者就会调用LoadingService。 UX组件订阅公开的observables ......

Spock answered 2019-08-13T13:42:11Z
1 votes

我保存了诸如:: ::

isFetching: {
    /api/posts/1: true,
    api/posts/3: false,
    api/search?q=322: true,
}

然后我有一个记忆选择器(通过重新选择)。

const getIsFetching = createSelector(
    state => state.isFetching,
    items => items => Object.keys(items).filter(item => items[item] === true).length > 0 ? true : false
);

为了在POST的情况下使url唯一,我将一些变量作为查询传递。

在我想要显示指标的地方,我只使用getFetchCount变量

Sergiu answered 2019-08-13T13:43:15Z
translate from https://stackoverflow.com:/questions/35456935/how-to-show-a-loading-indicator-in-react-redux-app-while-fetching-the-data