如何在数据准备好后呈现组件?

时间:2016-07-19 14:43:14

标签: javascript reactjs promise

我正在试图弄清楚如何在数据准备好时填充/渲染组件?基本上我有一个查询我的服务器的脚本返回数据,然后我解析它并使其成为一个具有我需要的属性的集合。然后在另一个文件中,我有反应组件正在寻找该对象,但它们同时运行,因此当组件正在寻找它时该对象不存在。

我不知道该怎么办。

这是我的组成部分:

let SliderTabs = React.createClass({
    getInitialState: function() {
        return { items: [] }
    },
    render: function() {
        let listItems = this.props.items.map(function(item) {
            return (
                <li key={item.title}>
                    <a href="#panel1">{item.title}</a>
                </li>
            );
        });

    return (
            <div className="something">
                <h3>Some content</h3>
                    <ul>
                        {listItems}
                    </ul>
            </div>
        );
    }
});

ReactDOM.render(<SliderTabs items={home.data.slider} />,                
    document.getElementById('slider-tabs'));

我如何获取数据:

var home = home || {};

home = {
  data: {
    slider: [],
    nav: []
  },
  get: function() {

    var getListPromises = [];

    $.each(home.lists, function(index, list) {
      getListPromises[index] = $().SPServices.SPGetListItemsJson({
        listName: home.lists[index].name,
        CAMLViewFields: home.lists[index].view,
        mappingOverrides: home.lists[index].mapping
      })
      getListPromises[index].list = home.lists[index].name;
    })

    $.when.apply($, getListPromises).done(function() {
      home.notice('Retrieved items')
      home.process(getListPromises);
    })
  },
  process: function(promiseArr) {
    var dfd = jQuery.Deferred();

    $.map(promiseArr, function(promise) {
      promise.then(function() {
        var data = this.data;
        var list = promise.list;

        // IF navigation ELSE slider
        if (list != home.lists[0].name) {
          $.map(data, function(item) {
            home.data.nav.push({
              title: item.title,
              section: item.section,
              tab: item.tab,
              url: item.url.split(",")[0],
              path: item.path.split("#")[1].split("_")[0]
            })
          })
        } else {
          $.map(data, function(item) {
            home.data.slider.push({
              title: item.title,
              url: item.url.split(",")[0],
              path: item.path.split("#")[1]
            })
          })
        }
      })
    })

    console.log(JSON.stringify(home.data))
    dfd.resolve();
    return dfd.promise();
  }
}

$(function() {
  home.get()
})

6 个答案:

答案 0 :(得分:1)

在React中执行此操作的常用方法是跟踪何时获取数据。这可以通过例如完成。通过在您的州内设置isFetching字段:

// This would be your default state
this.state = {
  isFetching: false
};

然后,当您触发请求时(最好在componentDidMount中),您可以使用以下命令将isFetching设置为true:

this.setState({ isFetching: true });

最后,当数据到达时,再次将其设置为false:

this.setState({ isFetching: false });

现在,在渲染功能中,您可以执行以下操作:

render () {
 return (
    <div className="something">
      <h3>Some content</h3>
      {this.state.isFetching ? <LoadingComponent /> : (
         <ul>
           {listItems}
         </ul>
      )}
    </div> 
  )
}

通过使用州,您不必担心告诉您的组件执行某些操作,而是对状态中的更改做出反应并相应地呈现它。

<强>更新

如果您计划实际使用React,我建议您将方法改为我上面所述(请参阅the React docs中的更多内容)。也就是说,将您在get函数中的代码移动到React组件的componentDidMount函数中。如果那不可能,您可以等待致电

ReactDOM.render(
  <SliderTabs items={home.data.slider} />,                
  document.getElementById('slider-tabs')
);

直到您的数据到达。

答案 1 :(得分:0)

您应该测试数据收集的长度。如果集合为空,则返回占位符(例如加载轮)。在其他情况下,您可以照常显示数据集合。

const SliderTabs = ({items}) => {
    let listItems = <p>Loading data...</p>

    if(items.length != 0) 
        listItems = items.map(item => 
            <li key={item.title}>
                <a href="#panel1">{item.title}</a>
            </li>
        )

    return (
        <div className="something">
            <h3>Some content</h3>
                <ul>
                    {listItems}
                </ul>
        </div>
    )
}

ReactDOM.render(
    <SliderTabs items={home.data.slider} />,                
    document.getElementById('slider-tabs')
)

我已经使用功能方式来定义React组件,因为它是您不需要状态,参考或生命周期方法时的推荐方式。

如果你想在ES6 classe或React.createCompnent中使用它(应该避免),只需使用该函数作为渲染函数。 (别忘了从道具中提取items

编辑:通过阅读新的答案,我意识到我还没有完全回答。

如果要在加载数据时更新视图,则必须集成更多的数据获取代码。 React中的基本模式是将您的组件分为两种类型:容器组件和演示组件。

容器只会处理逻辑并获取有用的数据。另一方面,演示组件仅显示容器给出的数据。

这里有一个小例子:(try it on jsfidle)

测试实用程序

var items = [{title: "cats"},{title: "dogs"}]

//Test function faking a REST call by returning a Promise.
const fakeRest = () => new Promise((resolve, reject) =>
  setTimeout(() => resolve(items), 2000)
)

容器组件

//The Container Component that will only fetch the data you want and then pass it to the more generic Presentational Component
class SliderTabList extends React.Component {
  constructor(props) { //
    super(props)
    //You should always give an initial state (if you use one of course)
    this.state = { items : [] }
  }

  componentDidMount() {
    fakeRest().then(
      items => this.setState({ items }) //Update the state. This will make react rerender the UI.
    )
  }

  render() {
    //Better to handle the fact that the data is fetching here.
    //This let the Presentational Component more generic and reusable
    const {items} = this.state
    return (
      items.length == 0 
        ? <p>Loading Data...</p>
        : <List items={items} />
    )
  }
}

演示文稿

//The Presenational Component. It's called List because, in fact, you can reuse this component with other Container Component. Here is power of React.
const List = ({items}) => {
    //Prepare the rendering of all items
    const listItems = items.map(item => 
      <li key={item.title}>
        <a href="#panel1">{item.title}</a>
      </li>
    )

    //Simply render the list.
    return (
      <div className="something">
        <h3>Some content</h3>
          <ul>
            {listItems}
          </ul>
      </div>
    )
}

渲染应用

//Mount the Container Component. It doesn't need any props while he is handling his state itself
ReactDOM.render(
    <SliderTabList />,                
    document.getElementById('slider-tabs')
)

您可以在状态中初始化itemsnull,而不是检查长度是否为0,以便能够区分从空数据中获取数据。另一种常见的方法是将一个标志(状态为fetchingData中的布尔值)设置为知道数据是否正在获取。但是,在很多文章中,我们通常建议尽可能使用一个州,然后从中计算出你需要的所有东西。所以在这里,我要求您检查长度或null

答案 2 :(得分:0)

Here是React执行这些类型事务的方式的解释,tl; dr - 立即渲染组件并显示加载指示符直到数据准备好或从{{返回null 1}}方法。

答案 3 :(得分:0)

将数据加载到父组件中,以便在加载数据时更新组件的道具。

使用默认道具而不是默认状态,因为您的示例中根本没有使用状态。将'getInitialState'替换为:

   getDefaultProps: function() {
     return {
       items: []
     };
   }

答案 4 :(得分:0)

我得到了一些答案,但我仍然很难理解如何完成我的要求。我知道我应该使用组件检索数据,但我目前还不了解React做到这一点。我显然需要花更多的时间来学习它,但是现在我接受了这个:

基本上,我将属性ready添加到我的主对象:

home.ready: false,
home.init: function() {
        // check if lists exist
        home.check()
        .then(home.get)
        .then(function() {
          home.ready = true; 
        })
    }

然后我使用componentDidMountsetTimeout检查数据何时准备就绪,并将结果设置为this.state

let SliderTabs = React.createClass({
    getInitialState: function() {
        return {items:[]}
    },
    componentDidMount: function() {
        let that = this;

        function checkFlag() {
           if(home.ready == false) {
              window.setTimeout(checkFlag, 100); /* this checks the flag every 100 milliseconds*/
           } else {
              that.setState({items: home.data.slider})
           }
        }
        checkFlag();
    },
    render: function() {
        let listItems = this.state.items.map(function(item) {
            return (
                <li key={item.title}>
                    <a href="#panel1">{item.title}</a>
                </li>
                );
        });

        return (
            <div className="something">
                <h3>Some content</h3>
                <ul>
                    {listItems}
                </ul>
            </div>
            );
    }
});

ReactDOM.render(<SliderTabs/>,        
    document.getElementById('slider-tabs'));

可能不是React的方式,但似乎有效。

答案 5 :(得分:-2)

通常情况下,您会安排对OnLoad事件的回复,该事件将在数据加载后“触发”。

我同意Emrys的说法,你的代码准备测试数据是否可用,并在初始绘制时“做一些合理的”(当它可能 isn'时吨)。但是,不会,您不会“轮询”以查看数据是否已到达:相反,您安排在事件触发时收到通知。那时,你会(例如)重新绘制 UI组件以反映信息。

我恳请您参考React文档,以确切了解此通知是如何完成的......