【问题标题】:Conceptually, how do I write my flux Stores to not mix up unrelated data together?从概念上讲,我如何编写通量存储以不将不相关的数据混合在一起?
【发布时间】:2016-02-05 17:05:54
【问题描述】:

假设我有一个依赖于 Flux 的应用程序。

大多数 Flux 教程告诉您,您的 Components 应该在 Store 上侦听 change 事件并相应地呈现数据。这种方法非常适合小而简单的用例,但是我在稍微复杂一点的场景中遇到了问题:

我有一个可以按居住城市(华盛顿、纽约等)过滤的客户列表。此列表在 url /customers/by-city 下可用,可以用以下代码表示:

var CustomersList = React.createClass({
 
  getInitialState() {
    return {
      city: 'Washington',
      customers: []
    };
  },
 
  componentWillMount: function() {
    CustomersActions.retrieveForCity(this.state.city);
    CustomersStore.on('change', this.handleNewData);
  },

  componentWillUnmount: function() {
    CustomersStore.off('change', this.handleNewData);
  },

  render: function() {
    return <FilteredTable filters={['city']}
                          onCityChange={this.handleCityChange} 
                          data={this.state.customers} />;
  },

  handleCityChange: function(state) {
    CustomersActions.retrieveForCity(this.state.city);
    this.setState({city: city});
  },
  
  handleNewData: function(customers) {
    this.setState({customers: customers});
  }

});

每次我想按不同城市过滤时,CustomersActions 发出 AJAX 请求,响应转到DispatcherCustomersStore 然后拿起它,存储为 data,并发出 change 事件。几乎是标准 Flux 的东西。

现在,据我了解,没有办法将 change 事件与其实际所做的事情联系起来。所以想象一下下面的场景:

  • 我通过San Francisco过滤表格
  • 我改变了主意,在 AJAX 请求完成之前,我点击了一些导航链接(由 react 处理,没有页面重新加载),我最终访问了一个不同的 url,例如/customers/shortlisted。此 URL 下还有另一个组件:ShortlistedCustomers
  • ShortlistedCustomers 类似于 CustomersList - 为了显示数据,它通过 CustomerActions 发出 ajax 请求并在 CustomersStore 上侦听 change 事件
  • 现在的技巧部分 - 第二个 ajax 请求在第一个之前完成,发出两个 change 事件,用户最终在他的表中得到错误的数据

如何“正确”解决这个问题?我对最简单的解决方案感兴趣,也许是 FB 人自己推荐的?我意识到我可以例如取消第一个 AJAX,或者只按照发出这些请求的顺序处理响应,但这很快就会失控。

请注意,此问题并非针对此案例,而是一个更普遍的问题。例如。如果我想添加更多方式来触发 AJAX 请求,或者更多依赖于同一个 Store 的组件,则该解决方案应该适用。

【问题讨论】:

  • “我转到我的应用程序中的另一个组件” --- 这个“go”实际上是如何表示的?
  • 我更新了我的问题 - 这个“go”可能是 react-router 或类似的东西;如果这就是您的意思,则不会重新加载页面
  • "在您当前的代码中,您从一个具有 2 个属性的对象开始,然后将其替换为具有一个属性的对象。" setState() 的第一个参数是差异,而不是新状态;所以它被合并到现有状态中
  • facebook.github.io/react/docs/component-api.htmlsetState() Performs a shallow merge of nextState into current state
  • 天哪,我一直认为它每次都会替换整个对象:-S 道歉

标签: javascript reactjs flux


【解决方案1】:

在更复杂的应用程序中,使用 Reflux 或 Redux 可能会做得更好。使用 Reflux,您可以发出参数,让控制组件根据需要路由数据。

import Reflux from 'reflux';

import Actions from './Actions';
import AddonStore from './Addon.Store';
import MixinStoreObject from './Mixin.Store';

function _GotData(data) { this.data1 = data; BasicStore.trigger('data1'); }

let BasicStoreObject = {
	init() { this.listenTo(AddonStore, this.onAddonTrigger); },
	data1: {},
	listenables: Actions,
	mixins: [MixinStoreObject],
	onGotData1: _GotData,
	onAddonTrigger() { BasicStore.trigger('data2'); },
	getData1() { return this.data1; },
	getData2() { return AddonStore.data2; },
	getData3() { return this.data3; }
}
const BasicStore = Reflux.createStore(BasicStoreObject);
export default BasicStore;

import React from 'react';

import BasicStore from './../flux/Basic.Store';

let AppCtrlSty = {
	height: '100%',
	padding: '0 10px 0 0'
}

const getState = () => {
	return {
		Data1: BasicStore.getData1(),
		Data2: BasicStore.getData2(),
		Data3: BasicStore.getData3()
	};
};

class AppCtrlRender extends React.Component {
 	render() {
		let data1 = JSON.stringify(this.state.Data1, null, 2);
		let data2 = JSON.stringify(this.state.Data2, null, 2);
		let data3 = JSON.stringify(this.state.Data3, null, 2);
		return (
			<div id='AppCtrlSty' style={AppCtrlSty}>
				React 1.4 ReFlux with SuperAgent<br/><br/>
				Data1: {data1}<br/><br/>
				Data2: {data2}<br/><br/>
				Data3: {data3}<br/><br/>
			</div>
		);
	}
}

export default class AppCtrl extends AppCtrlRender {
	constructor() {
		super();
		this.state = getState();
	}

	componentDidMount() { this.unsubscribe = BasicStore.listen(this.storeDidChange.bind(this)); }
	componentWillUnmount() { this.unsubscribe(); }
	storeDidChange(id) {
		switch (id) {
			case 'data1': this.setState({Data1: BasicStore.getData1()}); break;
			case 'data2': this.setState({Data2: BasicStore.getData2()}); break;
			case 'data3': this.setState({Data3: BasicStore.getData3()}); break;
			default: this.setState(getState());
		}
	}
}

【讨论】:

    【解决方案2】:

    当然,有几种可行的方法来解决这种情况,每种方法都有自己的优缺点。这里有一些我认为仍然符合 Flux 的。

    请记住,我不知道您的应用的全部范围,因此其中一些可能对您不可行。

    CustomersStore 侦听已调度的 API 请求操作

    1. 调度一个动作以表明 API 请求已发送
    2. CustomersStore 在分派此操作时清除其状态

    也可用于创建加载状态。

    CustomersStore 将其响应存储在存储桶中

    1. 为 /by-city 端点请求的数据存储在 CustomersStore 中的“byCity”哈希下。
    2. /by-city 的组件现在通过指定此哈希来访问其数据,例如 CustomersStore.getData('byCity')
    3. /shortlisted 的数据存储和检索方式相同

    现在你可以利用一些缓存逻辑了。

    创建操作以重置 CustomersStore

    1. 当 CustomersStore 收到此“重置”操作时,它将清除其存储的数据。
    2. /by-city 和 /shortlisted 组件在相应的生命周期函数中调度“重置”操作。

    这也将帮助您在单个组件中处理同样的问题

    这些想法很少,我相信它们仍然会遵守 Flux 原则。希望这有用!

    【讨论】:

    • “Buckets”方法是有道理的,但是它不能保护用户免受相同存储桶竞争条件的影响,例如我过滤了 CustomersList,转到其他地方,然后在初始 AJAX 完成之前返回
    【解决方案3】:

    ShortListedCustomersCustomersList 应该从不同的商店[或从公共商店中获取不同的属性]/“获取他们的数据”

    当操作/存储触发数据已到达的事件时,只有当前渲染的组件会使用适合它的数据重新渲染。

    (话虽这么说,我相信你应该转向 redux——甚至是逐渐地——reducer 中的不变性和排序将帮助你解决这类问题)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-04-06
      • 2019-06-17
      • 2023-03-22
      • 2013-11-26
      • 1970-01-01
      • 2011-09-30
      • 2010-11-15
      • 2011-07-24
      相关资源
      最近更新 更多