【问题标题】:How do I render a component when the data is ready?数据准备好后如何渲染组件?
【发布时间】:2016-07-23 03:03:50
【问题描述】:

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

我不确定如何继续。

这是我的组件:

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()
})

【问题讨论】:

  • 你有研究过 Redux 吗?这是跟踪状态和处理数据的最简单方法
  • 我还没有,我今天才开始研究 React,所以我还不太熟悉。
  • 准备好数据后,只需使用setState。应该就这么简单。不要进入 Redux。初学者真的不需要。正如 React 经常提到的,render 应该是props(外部)+state(内部)的函数。
  • 我会有一个父组件,它使用将数据设置为状态变量,然后将其作为道具传递给子组件
  • @activatedgeek 我很难看到this.setState 如何与我的组件一起工作。是全局变量吗? home.get() 是否应该在完成后更改状态?

标签: javascript reactjs promise


【解决方案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')
);

直到您的数据到达。

【讨论】:

  • 你把this.statesetState放在哪里? home.get() 不需要改变状态吗?我不确定home.get() 如何更改组件状态标志。除非它是全球性的?
  • 如果你真的不想使用 React 获取数据(如果可能的话,我建议你这样做),那么你不能使用我所描述的。请参阅我的更新答案。
【解决方案2】:

Here 是对 React 做这些事情的方式的解释,tl;dr - 立即渲染组件并显示加载指示器直到数据准备好或从 render 方法返回 null

【讨论】:

  • 您发送的链接显示了如果组件本身正在加载数据但我正在获取数据并将其存储在反应之外的情况下如何做到这一点。
  • 我会说首先创建一个组件并在其中为您的数据保留占位符并在数据到达后填充它们而不是等到ajax请求完成并再次等待组件更有效被创建和渲染。我认为行为应该是异步的。
【解决方案3】:

将数据加载放在父组件中,在加载数据时更新组件的 props。

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

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

【讨论】:

    【解决方案4】:

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

    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 类或 React.createCompnent 中使用它(应该避免),只需使用该函数作为渲染函数。 (不要忘记从道具中提取items


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

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

    容器只会处理逻辑并获取有用的数据。另一方面,Presentational Components 只会显示 Container 提供的数据。

    这里有一个小例子:(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')
    )
    

    除了检查长度是否不为0,您还可以在状态下将items初始化为null,以便能够区分取数据和空数据。另一种常用的方式来放置标志(fetchingData int 状态中的布尔值)以了解数据是否正在获取。但是,在很多文章中,一般建议拥有一个尽可能少的状态,然后从中计算出你需要的一切。所以在这里,我建议你检查长度或null

    【讨论】:

    • 它会一直检查自己吗?
    • 我在小提琴中尝试过这是一个更基本的实现,无需等待数据,不起作用。 jsfiddle.net/69z2wepo/49604
    • 对不起,我忘记删除括号了:jsfiddle.net/69z2wepo/49615
    • @Batman 用更完整的示例和有效的 jsfidle 编辑了我的答案。
    【解决方案5】:

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

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

    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 方式,但它似乎有效。

    【讨论】:

      【解决方案6】:

      通常,您会安排对 OnLoad 事件的响应,该事件将在数据加载后“触发”。

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

      请您参阅 React 文档,以了解此通知的具体完成方式...

      【讨论】:

        猜你喜欢
        • 2020-08-07
        • 2020-03-28
        • 2012-12-26
        • 1970-01-01
        • 1970-01-01
        • 2017-07-01
        • 2017-07-06
        • 2018-07-23
        • 2016-10-31
        相关资源
        最近更新 更多