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

我是React / Redux的新手。 我在Redux应用程序中使用获取api中间件来处理API。 它是https://github.com/agraboso/redux-api-middleware 。 我认为这是处理asynchronousAPI行为的好方法。 但是我发现了一些自己无法解决的情况。

正如主页https://github.com/agraboso/redux-api-middleware#lifecycle所言,抓取API生命周期从调度CALL_API动作开始,然后调度FSA动作。

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

但如何做到这一点? 一个反应组件在整个页面上stream动? 从其他操作更新商店会发生什么情况? 我的意思是说他们比国家更像事件!

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

最好的祝愿! 希望回复。

我的意思是说他们比国家更像事件!

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

在Redux isFetchingasync示例中 ,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中的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 } } 

最后,组件在render()函数中使用isFetching prop来呈现一个“Loading …”标签(可以想象它是一个微调isFetching )。

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

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

任何副作用(并显示对话肯定是副作用)不属于reducer。 认为减速机是被动的“国家build设者”。 他们并不真正“做”的事情。

如果您希望显示警报,请在分派操作之前从组件执行此操作,或者从操作创build者执行此操作。 在发布行动的时候,为了回应这个问题,执行副作用为时已晚。

对于每一条规则,都有一个例外。 有时候,你的副作用逻辑如此复杂,你实际上想把它们与特定的动作types或特定的reducer结合起来。 在这种情况下,请查看Redux Saga和Redux Loop等高级项目。 只有当你对香草Redux感到舒适时,才能做到这一点,并有一个真正的问题,散落的副作用,你想更容易pipe理。

我想添加一些东西。 现实世界中的示例使用商店中的一个字段“ isFetching ”来表示正在提取项目集合的时间。 任何集合被推广到一个pagination器,可以连接到您的组件跟踪状态,并显示是否加载一个集合。

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

为了解决这个问题,我添加了一个名为fetching通用fetching 。 它与分页缩减器的工作方式类似,只是要观察一组动作并使用pair [entity, isFetching]生成新的状态。 这允许connect reducer connect到任何组件,并知道该应用程序当前是否正在提取数据,而不仅仅是一个集合,而是一个特定的实体。

很好的答案Dan Abramov! 只是想补充说,我正在做一个或多或less正是在我的一个应用程序(保持作为一个布尔值),并最终不得不使其成为一个整数(最终读为未完成的请求的数量),以支持多个同时要求。

用布尔值:

请求1开始 – >微调开启 – >请求2开始 – >请求1结束 – >微调closures – >请求2结束

整数:

请求1开始 – >微调开启 – >请求2开始 – >请求1结束 – >请求2结束 – >微调closures

 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 

直到现在我还没有发生这个问题,但是由于没有答案被接受,所以我会抛出我的帽子。 我为这个工作写了一个工具: react-loader-factory 。 它比阿布拉莫夫的解决scheme稍微复杂一些,但更加模块化和方便,因为我不想在写完之后思考。

有四大块:

  • 工厂模式:这使您可以快速调用相同的function来设置组件的“装载”状态,以及分派哪些操作。 (这里假定组件负责启动等待的动作。) const loaderWrapper = loaderFactory(actionsList, monitoredStates);
  • 包装:工厂生产的组件是一个“更高级的组件”(比如Redux中的connect()返回的内容),这样你就可以将其绑定到你现有的东西上。 const LoadingChild = loaderWrapper(ChildComponent);
  • Action / Reducer交互:包装器检查是否有插入的reducer包含关键字,告诉它不要传递给需要数据的组件。 包装器派发的动作需要产生关联的关键字(例如, ACTION_REQUEST -api-middleware分派ACTION_SUCCESSACTION_REQUEST )。 (当然,如果你想的话,你也可以在其他地方调度动作,只需要从包装器中监控)。
  • Throbber:当组件依赖的数据尚未准备就绪时,您想要显示的组件。 我在那里添加了一个小的div,所以你可以testing一下,而不需要进行修改。

模块本身独立于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 } />; } 

装载机的监视器(尽pipe你可以用不同的方式连接 ):

 export function activeRequests(state = [], action) { const newState = state.slice(); // regex that tests for an API action string ending with _REQUEST const reqReg = new RegExp(/^[AZ]+\_REQUEST$/g); // regex that tests for a API action string ending with _SUCCESS const sucReg = new RegExp(/^[AZ]+\_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. 其余的直觉对我来说似乎是正确的。

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

alertconfirm应该不成问题。 他们阻止和警报甚至不接受来自用户的任何input。 confirm ,如果用户select会影响组件渲染,则可以根据用户点击的内容设置状态。 如果不是,则可以将select存储为组件成员variables以供以后使用。

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

现在,我与Angular2合作,我所做的是我有一个“加载”服务,通过RxJS BehaviourSubjects公开不同的加载指标。我想机制是相同的,我只是不存储在Redux中的信息。

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

每当需要更改时,我的Redux动作创build者都会调用LoadingService。 UX组件订阅暴露的observables …

我们的应用程序中有三种types的通知,所有这些通知均被devise为以下几个方面:

  1. 加载指标(基于道具的模态或非模态)
  2. popup错误(模态)
  3. 通知零食(非模态,自闭)

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

我devise了一个代理来处理我们所有的API调用,因此所有的捕获和(api)错误都是由我在代理中导入的actionCreators调解的。 (另外,我还使用webpack为dev开辟了一个支持服务的模拟,所以我们可以在没有服务器依赖的情况下工作)。

需要提供任何types的通知的应用程序中的任何其他地方只是导入适当的操作。 Snackbar&Error对于要显示的消息有参数。

 @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) }} 

)导出默认类Main扩展React.Component {

我保存的url如::

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

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

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

为了使POST的情况下唯一的URL,我传递一些variables作为查询。

而我想要显示一个指标的地方,我只需使用getFetchCountvariables