如何使用React + Flux快速渲染> 10000个项目?

时间:2015-11-27 14:40:37

标签: javascript checkbox reactjs flux refluxjs

我想问一下快速渲染的正确方法> React中有10000件商品。

假设我想创建一个checkboxList,包含动态10000复选框项

我创建了一个包含所有项目的商店,它将用作复选框列表的状态。

当我点击任何复选框项目时,它会按操作更新相应的项目,因此商店会更改。

由于商店已更改,因此会触发复选框列表更新。

复选框列表更新其状态并再次渲染。

这里的问题是,如果我点击任何复选框项目,我必须等待>勾选复选框3秒钟。我不希望这只需要重新渲染一个复选框项。

我试图找到根本原因。 最耗时的部分是在复选框列表渲染方法中,与.map相关,它创建Checkbox组件以形成componentList .. 但实际上只有1个复选框必须重新渲染。

以下是我的代码。 我使用ReFlux作为助焊剂架构。

CheckboxListStore

商店将所有复选框项目存储为地图。 (名称为键,状态(true / false)为值)

const Reflux = require('reflux');
const Immutable = require('immutable');
const checkboxListAction = require('./CheckboxListAction');

let storage = Immutable.OrderedMap();
const CheckboxListStore = Reflux.createStore({
	listenables: checkboxListAction,
	onCreate: function (name) {
		if (!storage.has(name)) {
			storage = storage.set(name, false);
			this.trigger(storage);
		}
	},
	onCheck: function (name) {
		if (storage.has(name)) {
			storage = storage.set(name, true);
			this.trigger(storage);
		}
	},
	onUncheck: function (name) {
		if (storage.has(name)) {
			storage = storage.set(name, false);
			this.trigger(storage);
		}
	},
	getStorage: function () {
		return storage;
	}
});

module.exports = CheckboxListStore;

CheckboxListAction

操作,创建,检查和取消选中任何带有名称的复选框项。

const Reflux = require('reflux');
const CheckboxListAction = Reflux.createActions([
	'create',
	'check',
	'uncheck'
]);
module.exports = CheckboxListAction;

的CheckBoxList

const React = require('react');
const Reflux = require('reflux');
const $ = require('jquery');
const CheckboxItem = require('./CheckboxItem');
const checkboxListAction = require('./CheckboxListAction');
const checkboxListStore = require('./CheckboxListStore');
const CheckboxList = React.createClass({
	mixins: [Reflux.listenTo(checkboxListStore, 'onStoreChange')],
	getInitialState: function () {
		return {
			storage: checkboxListStore.getStorage()
		};
	},
	render: function () {
		const {storage} = this.state;
		const LiComponents = storage.map((state, name) => {
			return (
				<li key = {name}>
					<CheckboxItem name = {name} />
				</li>
			);
		}).toArray();
		return (
			<div className = 'checkbox-list'>
				<div>
					CheckBox List
				</div>
				<ul>
					{LiComponents}
				</ul>
			</div>
		);
	},
	onStoreChange: function (storage) {
		this.setState({storage: storage});
	}
});

module.exports = CheckboxList;

CheckboxItem 在onChange回调中,我调用动作来更新项目。

const React = require('react');
const Reflux = require('reflux');
const $ = require('jquery');
const checkboxListAction = require('./CheckboxListAction');
const checkboxListStore = require('./CheckboxListStore');

const CheckboxItem = React.createClass({
	mixins: [Reflux.listenTo(checkboxListStore, 'onStoreChange')],
	propTypes: {
		name: React.PropTypes.string.isRequired
	},
	getInitialState: function () {
		const {name} = this.props;
		return {
			checked: checkboxListStore.getStorage().get(name)
		};
	},
	onStoreChange: function (storage) {
		const {name} = this.props;
		this.setState({
			checked: storage.get(name)
		});
	},
	render: function () {
		const {name} = this.props;
		const {checked} = this.state;
		return (
			<div className = 'checkbox' style = {{background: checked ? 'green' : 'white'}} >
				<span>{name}</span>
				<input ref = 'checkboxElement' type = 'checkbox'
					onChange = {this.handleChange}
					checked = {checked}/>
			</div>
		);
	},
	handleChange: function () {
		const {name} = this.props;
		const checked = $(this.refs.checkboxElement).is(':checked');
		if (checked) {
			checkboxListAction.check(name);
		} else {
			checkboxListAction.uncheck(name);
		}
	}
});

module.exports = CheckboxItem;

5 个答案:

答案 0 :(得分:2)

您可以采取一些方法:

  1. 不渲染所有10,000 - 只根据面板大小和滚动位置渲染可见复选框(+更多),并处理滚动事件以更新可见子集(使用组件状态,而不是通量) 。您需要以某种方式处理滚动条,方法是手动渲染滚动条,或者通过使用普通浏览器滚动条在顶部和底部添加巨大的空div来替换您未渲染的复选框,从而更轻松地处理滚动条。滚动条位于正确的位置。这种方法允许您处理100,000个复选框甚至一百万个复选框,第一个渲染是快速的以及更新。可能是首选的解决方案。这里有很多这种方法的例子:http://react.rocks/tag/InfiniteScroll
  2. 微优化 - 您可以执行storage.toArray().map(...)(这样您就不会创建中间映射),甚至更好,制作并清空数组,然后执行storage.forEach(...)添加元素推 - 快得多。但是,React diffing算法仍然需要对10000个元素进行区分,这种方法永远不会很快,但是生成元素的代码要快得多。
  3. 以某种方式将巨大的地图拆分成块,这样当你检查一个chechbox时,只有一个块会发生变化。还以相同的方式(进入CheckboxListChunks)或类似方式拆分React组件。这样,只需要为每个块都有一个PureComponent类型的componentShouldUpdate函数,你只需要重新渲染已更改的块(可能Reflux会为你做这个吗?)。
  4. 远离基于ImmutableJS的变量,因此您可以更好地控制何时更改(例如,您不必更新父复选框映射,因为其中一个子项已更改)。
  5. 将自定义shouldComponentUpdate添加到CheckboxList:

    shouldComponentUpdate:function(nextProps, nextState) {
        var storage = this.state.storage;
        var nextStorage = nextState.storage;
        if (storage.size !== nextStorage.size) return true;
        // check item names match for each index:
        return !storage.keySeq().equals(nextStorage.keySeq());
    }
    

答案 1 :(得分:1)

是的,我放弃了变化... 我终于决定使用mobservable来解决我的问题。 我举了一个例子https://github.com/raymondsze/react-example 请参阅https://github.com/raymondsze/react-example/tree/master/src/mobservable进行编码。

答案 2 :(得分:1)

除了初始渲染之外,您还可以使用Mobservable显着提高大型集合的渲染速度。当孩子通过自动应用副作用加载进行更改时,它可以避免重新渲染映射到10.000个项目的父组件。有关详细说明,请参阅此blog

答案 3 :(得分:0)

你的渲染功能看起来有点复杂,需要:

  • 它首先生成一个JSX组件数组
  • 然后转换应用(jQuery?).toArray()
  • 然后返回这个新生成的数组。

将渲染函数简化为类似的东西可能会有所帮助吗?

render: function () {
  return (
    <div className = 'checkbox-list'>
      <div>
        CheckBox List
      </div>
      <ul>
        {this.state.storage.map((state, name) => {
          return (
            <li key = {name}>
              <CheckboxItem name = {name} />
            </li>
          );
         })}
      </ul>
    </div>
  );
},

答案 4 :(得分:0)

每次检查/取消选中时,您是否真的需要在商店中保存支票状态?

我最近遇到了和你一样的问题。只需在CheckboxList组件的状态中保存checkedList数组[name1,name2 ...],并在每次选中/取消选中项目时更改此checkedList。如果要将检查状态保存到数据存储,请调用Action.save()并将checkedList传递给存储 但是,如果每次检查/取消选中时确实需要保存到数据存储,此解决方案将无法帮助-_-。