在决定渲染之前反应等待映射函数以防止div闪存

时间:2015-01-08 13:28:29

标签: javascript dictionary reactjs

我在防止div闪存方面遇到了一些麻烦,其中react将呈现错误消息,然后接收道具并最终呈现道具。

在我的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 Collection全局变量。

文档加载theseEvents将无法填充,因此我的视图会随<NoEventView />一起闪烁,然后在填充theseEvents时重新渲染。这由下面显示的主干mixin处理。

丹的解决方案让我指出了正确的方向。我将全局变量window.hasEvents设置为true或false,具体取决于我从ajax请求中返回的内容,但到那时React已经呈现(该组件将以hasEvents开始时为真,当时它已完成hasEvents可能是错误的)。即使我hasEvents <EventsView />支持hasEvents反应也不会在它发生变化时重新渲染。

因此,作为临时解决方法,我将EventsView默认为true并呈现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} } }); 。然后我等待组件加载并检查它是否与hasEvents同步。如下所示。老实说,这种解决方法并不是很愉快。

view.js

componentDidMount

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

2 个答案:

答案 0 :(得分:5)

你的第二种方法不是“React方式”(虽然技术上可能有效),因为你不应该把React元素置于状态。只有数据应该去那里,如果需要弄清楚数据如何与DOM结构相对应,则在render。除非绝对必要,否则最好避免使用state

话虽如此,我不太确定你想要实现的目标。假设您希望在事件发生之前阻止“无事件”闪存,则组件可以执行此操作的唯一方法是知道正在获取数据。除了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} />;
  });    
}

答案 1 :(得分:3)

由于我在我的应用程序开始检查用户是否经过身份验证时发出请求,这就是我在我的组件中处理它的方式。父组件状态链接到我的商店,它作为属性向下传递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}
            );
        }
    });