javascript - How do I render a component when the data is ready? - Stack Overflow

I'm trying to figure out how to populaterender a ponent when the data is ready? Essentially I hav

I'm trying to figure out how to populate/render a ponent when the data is ready? Essentially I have a script that queries my server which returns data, then I parse it and make it into an collection with the properties I need. Then in another file, I have the react ponent that's looking for that object but they're running at the same time so the object doesn't exist when the ponent is looking for it.

I'm not sure how to proceed.

This is my ponent:

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'));

How I'm getting my data:

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

I'm trying to figure out how to populate/render a ponent when the data is ready? Essentially I have a script that queries my server which returns data, then I parse it and make it into an collection with the properties I need. Then in another file, I have the react ponent that's looking for that object but they're running at the same time so the object doesn't exist when the ponent is looking for it.

I'm not sure how to proceed.

This is my ponent:

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'));

How I'm getting my data:

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()
})
Share Improve this question asked Jul 19, 2016 at 14:43 BatmanBatman 6,38322 gold badges88 silver badges161 bronze badges 5
  • Have you looked into Redux at all? That's the easiest way to keep track of state and work with data – erichardson30 Commented Jul 19, 2016 at 14:49
  • I haven't really, I just started looking at React today so I'm not too familiar with everything yet. – Batman Commented Jul 19, 2016 at 14:54
  • After you are ready with data, just use setState. Should be as simple as that. Don't do into Redux. It isn't really needed for beginners. And as React always mentions, render should be a function of props (external) + state (internal). – nyumerics Commented Jul 19, 2016 at 15:15
  • I would have a parent ponent that uses sets the data to a variable on state, then pass that down as a prop to the child ponent – Luke Schunk Commented Jul 19, 2016 at 15:35
  • @activatedgeek I'm having a hard time seeing how this.setState works with my ponent. Is it a global variable? Is home.get() suppose to change the state when it's pleted? – Batman Commented Jul 19, 2016 at 18:55
Add a ment  | 

6 Answers 6

Reset to default 3

A mon way to do this in React is to keep track of when data is being fetched. This can be done e.g. by having a isFetching field in your state:

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

Then, when you fire off the request (preferably in ponentDidMount) you set isFetching to true using:

this.setState({ isFetching: true });

And finally, when the data arrives, you set it to false again:

this.setState({ isFetching: false });

Now, in your render function you can do something like this:

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

By using state, you don't have to worry about telling your ponent to do something, instead it reacts to changes in the state and renders it accordingly.

Update:

If you plan on actually using React, I'd suggest you change your approach into what I've described above (read more in the React docs). That is, move the code you have in your get function into your React ponent's ponentDidMount function. If that's not possible, you can probably just wait to call

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

until your data has arrived.

Here is the explaination of React's way of doing these type of things, tl;dr - render the ponent immediately and either display loading indicator until the data is ready or return null from the render method.

Put that data loading in parent ponent that updates the props of your ponent as the data is being loaded.

Use default props instead of default state, since you are not using state at all in your example. Replace the 'getInitialState' with this:

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

You should test the length of the data collection. If the collection is empty, return a placeholder (a loading wheel for exemple). In other cases, you can display the data collection as usual.

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')
)

I have use the functional way to define a React Component as it's the remended way while you don't need of a state, refs or lifecycle methodes.

If you want to use this in a ES6 classe or with React.createCompnent (shoud be avoid), just use the function as the render function. (don't forget to extract items form the props)


EDIT : By reading the new answers, I've realised that I haven't fully answered.

If you want the view to be updated when the data are loaded, You have to integrate a little more your data fetching code. A basic pattern in React is to separate your ponents in tow type : the Containers Component and the Presentational Component.

The Containers will only take care of the logic and to fetch the useful data. In the other hand, the Presentational Components will only display the data given by the Container.

Here a little example : (try it on jsfidle)

Test utilities

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

Container Component

//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 : [] }
  }

  ponentDidMount() {
    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} />
    )
  }
}

Presentational Component

//The Presenational Component. It's called List because, in fact, you can reuse this ponent 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>
    )
}

Rendering the App

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

Rather than checking for the length to not being 0, you also can initialise items to null in the state, to be able to differentiate fetching data from empty data. An other mon way os to put a flag (a boolean in fetchingData int the state) to know if a data is fetching or not. But, in lots of articles, it's generaly reended to have a state as litle as possible and then calculate all you need from it. So here, I sugest you to check for the length or the null.

I got a few answers but I was still having a lot of trouble understanding how to acplish what I was asking for. I understand that I should be retrieving the data with the ponents but I currently don't know enough about React to do that. I obviously need to spend more time learning it but for now I went with this:

Essentially, I added the property ready to my home object:

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

Then I used ponentDidMount and a setTimeout to check when the data is ready and set the results to the this.state

let SliderTabs = React.createClass({
    getInitialState: function() {
        return {items:[]}
    },
    ponentDidMount: 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'));

Probably not the React way but it seems to work.

Ordinarily, you would arrange a response to an OnLoad event, which will "fire" once the data has been loaded.

I agree with Emrys that your code should also be prepared to test whether the data is available yet, and to "do something reasonable" on the initial draw (when it probably isn't). But no, you would not then "poll" to see if the data has arrived:   instead, you arrange to be notified when the event fires. At that time, you would (for example) re-draw the UI ponents to reflect the information.

I kindly refer you to the React documentation to see exactly how this notification is done ...

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744259332a4565552.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信