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

企业应*_*式大师 100 javascript reactjs redux

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

作为主页https://github.com/agraboso/redux-api-middleware#lifecycle比如说,一个取API生命周期开始派遣CALL_API行动派遣FSA行动结束.

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

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

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

最好的祝愿!希望回复.

Dan*_*mov 146

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

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

asyncRedux repo示例中,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
Run Code Online (Sandbox Code Playgroud)

该组件使用connect()从阵营终极版订阅商店的状态和收益isFetching作为的一部分mapStateToProps()返回值,以便它在连接组件的道具可供选择:

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

  return {
    selectedReddit,
    posts,
    isFetching,
    lastUpdated
  }
}
Run Code Online (Sandbox Code Playgroud)

最后,组件使用函数中的isFetchingproprender()来呈现"Loading ..."标签(可以想象它是一个微调器):

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

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

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

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

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

  • 如果我有多次提取怎么办?然而,一个变量是不够的. (16认同)
  • 请注意,如果请求失败,并且从未触发`RECEIVE_POSTS`,则加载符号将保留在原位,除非您创建了某种超时以显示"错误加载"消息. (3认同)
  • 请仔细查看我链接到的示例.有多个`isFetching`标志.它被设置为正在获取的每组对象.您可以使用reducer组合来实现它. (2认同)
  • @TomiS - 我明确地将我正在使用的任何redux持久性的所有isFetching属性列入黑名单. (2认同)

Nun*_*pos 19

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

用布尔值:

请求1开始 - >微调器开启 - >请求2开始 - >请求1结束 - >微调器关闭 - >请求2结束

带整数:

请求1开始 - >旋转器开启 - >请求2开始 - >请求1结束 - >请求2结束 - >旋转器关闭

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
Run Code Online (Sandbox Code Playgroud)

  • 是的!这取决于您是否要在UI中的一个或多个位置显示提取指示符.实际上,您可以将这两种方法结合起来,并为屏幕顶部的某个进度条和列表和页面的几个特定的​​`isFetching`标志设置*全局`fetchCounter`. (4认同)
  • 这是合理的.但是,大多数情况下,除了标志之外,您还希望存储一些*数据*.此时,您需要使用`isFetching`标志来拥有多个对象.如果仔细观察我链接到的示例,您将看到没有一个带有`isFetched`的对象,但很多:每个subreddit一个(在该示例中正在获取的内容). (2认同)
  • 哦。是的,我没有注意到。但是,在我的情况下,我在状态中有一个全局isFetching条目,并在其中存储了所提取数据的缓存条目,出于我的目的,我只真正在乎某些网络活动正在发生,对于什么并不重要 (2认同)

jav*_*sco 13

我想补充一些东西.真实世界示例使用isFetching商店中的字段来表示何时获取项目集合.任何集合都通用于paginationreducer,可以连接到组件以跟踪状态并显示集合是否正在加载.

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

为了解决这个我添加了另一个叫做通用减速fetching.它的工作方式与分页缩减器类似,它的职责就是观察一组动作并用对生成新的状态[entity, isFetching].这允许connectreducer到任何组件并且知道app当前是否正在获取数据而不仅仅是针对集合而是针对特定实体.

  • 谢谢你的回答!很少讨论处理单个物品的装载及其状态! (2认同)

bri*_*tar 5

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

有四大块:

  • 工厂模式:这允许您快速调用相同的功能来设置哪些状态意味着组件的"加载"以及要分派的操作.(这假定组件负责启动它等待的操作.)const loaderWrapper = loaderFactory(actionsList, monitoredStates);
  • 包装器:工厂生产的组件是一个"高阶组件"(就像connect()在Redux中返回的那样),因此您可以将它固定在现有的东西上.const LoadingChild = loaderWrapper(ChildComponent);
  • Action/Reducer交互:包装器检查它插入的reducer是否包含告诉它不要传递给需要数据的组件的关键字.由包装派出的行动预计将产生相关的关键字(顺便终极版的API中间件调度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 } />;
}
Run Code Online (Sandbox Code Playgroud)

要监控的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;
}
Run Code Online (Sandbox Code Playgroud)

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


你问题的简短回答是:

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


Spo*_*ck 5

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

现在,我使用 Angular2,我所做的是我有一个“加载”服务,它通过 RxJS BehaviourSubjects 公开不同的加载指标。我猜机制是一样的,我只是不将信息存储在 Redux 中。

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

我的 Redux 操作创建者在需要更改时调用 LoadingService。UX 组件订阅公开的 observables...

  • 嗨,一年多后回来检查,只是说我错了。当然UX状态属于应用状态。我能有多蠢? (22认同)

Mil*_*šić 3

您可以使用connect()React Redux 或低级store.subscribe()方法将更改监听器添加到您的存储中。您的商店中应该有加载指示器,商店更改处理程序可以检查并更新组件状态。然后,如果需要,该组件会根据状态呈现预加载器。

alert应该confirm不是问题。它们是阻塞的,警报甚至不接受用户的任何输入。使用confirm,如果用户选择会影响组件渲染,您可以根据用户单击的内容设置状态。如果没有,您可以将选择存储为组件成员变量以供以后使用。