React 最佳实践——纯组件还是非纯组件?在 componentWillMount 中获取数据?

React best-practices -- pure or non-pure components? fetching data in componentWillMount?

我在 Meteor 中摆弄 React。似乎有两种处理事情的方法...

以流星排行榜为例,我们有一个呈现姓名和分数的玩家列表。

做事的方式是获取所有玩家并传递到 playersList 组件,然后将其分配给 playerItem 组件。一切都是用道具完成的,这些模板是纯粹的。每当玩家发生变化时,我们都可以使用 Meteor 的反应性来使用 React

重新渲染所有内容
Tracker.autorun =>
  players = Players.find().fetch()
  React.render(React.createElement(PlayersList, {players:players}), document.body)

(传递给 autorun 的函数将在玩家更改时重新运行)

这几乎就是皮特·亨特在 Meteor-React presentation demo 中所做的。

现在,另一种方法似乎更有效、更精细,但不纯粹。即向 PlayersList 组件传递一个 playerId 列表。向 PlayerItem 组件传递一个 playerId 并让 PlayerItem 获取特定玩家并根据组件的状态呈现玩家姓名和分数。

// inside the PlayerItem component
componentWillMount: =>
  Tracker.autorun =>
    this.setState('player', Players.findOne(this.props.playerId))

现在 PlayerItem 不纯粹。每当玩家的分数发生变化时,只会重新渲染代表该玩家的 PlayerItem。如果我们确保只向 PlayersList 传递一个 playerId 列表,那么 PlayersList 组件只会在添加或删除玩家时重新呈现。

Tracker.autorun =>
  playerIds = _.pluck('_id',Players.find({}, {fields:{_id:1}}).fetch())
  React.render(React.createElement(PlayersList, {playerIds: playerIds}), document.body)

这是有道理的,因为现在我们对 React 的渲染周期有了细粒度的控制,而且我们不会在任何事情发生变化时对每个玩家的整个状态进行轰炸。然而,这似乎不符合 React 设计方式的精神。现在我们到处都有内部状态,我们的组件不纯!

我的印象是 React 希望我做事的方式是使用全局状态和纯组件,并且仅依赖于 React 的协调超级快速和高效这一事实。这让我有点不舒服,因为非纯粹的方式似乎更有效率。

无论如何,我刚开始使用 React,所以尽管我阅读了所有文档,但我不确定我知道我在说什么。所以我想知道——我什么时候应该坚持使用纯组件,什么时候可以使用非纯组件?我应该在组件中获取数据还是应该将 React 严格视为渲染引擎?

React.render 和在顶层组件中执行之间几乎没有区别。无论哪种方式,它都会更新状态或道具、渲染和区分 old/new 虚拟 dom。

我会让 PlayerList 获取所有玩家,并将它们传递给 PlayerItem 组件。您应该在 componentDidMount 中绑定到外部 API,并确保在 componentWillUnmount 中解除绑定。 componentWillMount 函数具有与构造函数类似的作用:设置任何非状态实例属性。

PlayerItem 应该实现 shouldComponentUpdate 以提高性能。您可以在有空闲时间的时候这样做,因为它是一种不会影响其余代码的优化。

附带说明一下,您可以实现一个有助于 meteor 绑定的 mixin。

var MeteorMixin = {
    getInitialState: function(){
        return this.getUpdatedState();
    },
    componentDidMount: function(){
        this._meteorMixinComputation = Tracker.autorun(function(){
            this.setState(this.getUpdatedState());
        }.bind(this));
    },
    componentWillUnmount: function(){
        this._meteorMixinComputation.stop();
    }
};

然后您的组件将如下所示:

mixins: [MeteorMixin],
getUpdatedState: -> {players: Players.find({}).fetch()}
render: ->
   ...

免责声明:我不使用 meteor,所以这可能不太合适。