在决定渲染之前反应等待地图功能以防止 div 闪光

React waiting for map function before deciding render to prevent div flash

我在阻止 div 闪光时遇到了一些麻烦,其中反应将呈现错误消息然后接收道具并最终呈现道具。

在我的 EventsView 组件中,我有以下内容。

view.js

var view;
if (_.size(this.props.events) !== 0) {
view =  this.props.events.map(function(year) {
                return <YearView year={year} />;
        });
} else {
    view = <NoEventsView />
}

所以变量 view 被渲染,最初页面加载时没有 this.props.events 因为我有文件的另一部分创建它。 this.props.events 创建的那一刻,事件被渲染。这是通过 react.Backbone mixin 完成的。

我尝试使用一些组件生命周期方法来做一些事情,例如 componentWillUpdate。问题是他们似乎不想按照我希望的方式工作,并且永远不会呈现错误消息。下面是我尝试过的示例(以及其他示例)

我想到了做这样的事情。

view.js

componentWillUpdate: function(nextprops, nextstate) {
if (_.size(nextprops.events) !== 0) {
    var view =  nextprops.events.map(function(year) {
                    return <YearView year={year} />;
            });
    this.setState({
        view: view
    });
} else {
    this.setState({
        view: <NoEventsView />
    })
}
},

// render this.state.view

然后我会将 getInitialState:view 设置为 this.props.events 地图。

*编辑 - 一个临时且糟糕的解决方法 *

所以我遇到的问题是我有一个顶级组件接受一个像这样的全局变量的道具

view.js

React.render(<EventsView events={theseEvents} />, document.getElementById('mainContent'));

theseEvents其实是在另一个文件中初始化的Backbone集合全局变量

在文档加载时 theseEvents 将被取消填充,因此我的视图将与 <NoEventView /> 一起闪烁,然后在 theseEvents 被填充时重新呈现。这是由下面显示的 backbone mixin 处理的。

Dan 的解决方案为我指明了正确的方向。我将全局变量 window.hasEvents 设置为 true 或 false,具体取决于我从 ajax 请求返回的内容,但到那时 React 已经在渲染(组件将开始为 hasEvents 为真,到它完成时 hasEvents 可能是错误的)。即使我将 hasEvents 设为 <EventsView /> 的道具,React 在更改时也不会重新渲染。

因此,作为临时解决方法,我将 hasEvents 默认为 true 并呈现 EventsView。然后我等待组件加载并检查它是否与 hasEvents 同步。如下所示。老实说,这个解决方法不是很令人愉快。

view.js

ar EventsView = React.createBackboneClass({
mixins: [
    React.BackboneMixin("events", "all")
],

getInitialState: function() {
    return {
        hasEvents: window.hasEvents
    }
},

componentDidMount: function() {
    var model = this;
    // This waits after component loading to check whether hasEvents is the same
    setTimeout(function() {
        if(!(model.state.hasEvents == window.hasEvents)) {
            model.setState({
                hasEvents: window.hasEvents
            })
        };
    }, 1000);
},

render: function() {
    var view;
    if (this.props.events.length > 0) {
        view =  this.props.events.map(function(year) {
                        return <YearView year={year} />;
                });
    }
    if (!this.state.hasEvents)  {
        view = <NoEventsView />
    }
    return {
        // other parts of the page were also rendered here
        {view}
    }
});

基本上componentDidMount在组件安装后等待一段时间,然后仔细检查全局变量。绝对不是一个很好的解决方法,特别是因为它依赖于 1 秒 theseEvents 填充并且它只适用于初始组件安装。

你的第二种方法不是“React 方式”(尽管从技术上讲它可能有效)因为你不应该将 React 元素放入状态。只有数据应该放在那里,如果需要弄清楚该数据如何对应于 DOM 结构,则在 render 中。除非绝对必要,否则最好避免 state

话虽这么说,但我不太确定您要实现的目标。假设您想在事件通过之前防止 "No Events" 闪烁,您的组件可以做到这一点的唯一方法是 知道 正在获取数据。如果,除了 events 属性,你传递 isLoading 布尔属性,你可以这样做:

render: function () {
  var isEmpty = this.props.events.length === 0;
  if (isEmpty) {
    return this.props.isLoading ?
      <p>Loading...</p> : // note you can also return null here to render nothing
      <NoEventsView />;
  }

  return this.props.events.map(function (year) {
    return <YearView year={year} />;
  });    
}

因为我在我的应用程序开始检查用户是否已通过身份验证时立即发出请求,这就是我在我的组件中处理它的方式。父组件状态链接到我的商店,它作为 属性.

向下传递 authenticated

我最初将组件 hasUpdated 状态设置为 false,因为它将从父级接收道具 我等到 componentWillRecieveProps 被触发,然后将 hasUpdated 设置为 true 因为这意味着我们的电话已经返回。然后它呈现它需要的元素。

不确定这是否是最好的方法,但到目前为止它已经满足了我们的需求。

var SomeComponent= React.createClass({
        displayName: 'SomeComponent',
        getInitialState: function() {
            return {
                hasUpdated : false
            };
        },
        getDefaultProps: function() {
            return {
                authenticated : false 
            };
        },
        componentWillReceiveProps: function(nextProps) {
            this.setState({ hasUpdated : true });
        },
        render: function() {
            var AuthElement;

            if(this.state.hasUpdated){
                if(this.props.authenticated){
                    AuthElement= <div>Authenticated</div>;
                }else{
                    AuthElement= <div>Not Authenticated</div>;
                }
            }

            return (
                {AuthElement}
            );
        }
    });