kriasoft / react-starter-kit

The web's most popular Jamstack front-end template (boilerplate) for building web applications with React
https://reactstarter.com
MIT License
22.78k stars 4.16k forks source link

wait for server data on Initial load #208

Closed simtechmedia closed 3 years ago

simtechmedia commented 9 years ago

Hey all,

I know this is a common scenario where people wishing to do isomorphic react want to wait for external data before initial load, has anyone been able to achieve this with the starter pack? I've been goggling around for hours without much success.

The scenario I want to achieve is that on initial load, wait for server JSON data coming from MongoDB before rendering, that way indexing can be done with data on the page, and there's no second call to get the data after initial load then business as usual once the initial load is done .

Cheers

Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

lehneres commented 9 years ago

I'm using the async keyword on my server route server.get( '*', async ( req, res, next ) => {...})) so I can use await within the method body. You can do the same with Promises.

In this way you can wait during the route process for external data, e.g. like JSON data. Then you can inject it into the HTML template to be send to the client or use an API endpoint to enable the client to receive the data

simtechmedia commented 9 years ago

Any chance of a code example?

I was trying to use await to wait for a request, but all the requests happen asynchronously using a callback so there's no way for me to to just wait for come back to feed the data as a prop to my component

lehneres commented 9 years ago

in my server.js I have a route (or middleware? not sure about the naming):

server.get( "/hotel/:uid", async( req, res, next ) => {
    try {
        const hotel = (await nekst.inflate( req.params.uid )).hotel;
        const place = (await nekst.getPlace( hotel.locationId )).place;
        const blog  = await nekst.getBlog( place );

        req.data = {hotel, place, blog};

        next();
    } catch ( err ) {
        next(err);
    }
} );

finally you could put this date into your template

router.dispatch( {path: req.path, context}, ( state, component ) => {
            data.body    = ReactDOM.renderToString( component );
            data.css     = css.join( '' );
            data.hotel  = req.data.hotel;
            data.place = req.data.place;
            data.blog  = req.data.blog;
        } );

        const html = template( data );

        res.status( statusCode ).send( html );

in your html template:

 <script>
        var hotel = <%= JSON.stringify(hotel) || JSON.stringify({}) %>;
        var place = <%= JSON.stringify(place) || JSON.stringify({}) %>;
        var blog = <%= JSON.stringify(blog) || JSON.stringify({}) %>;
    </script>

well, this is working, but I do not know for sure if it is good practice to store the date like this in the request. You could of course use any other global object as "transporter" but you would have to make sure it is "thread safe" or what ever has to be done in Javascript (which is not as you might see my homebase)

simtechmedia commented 9 years ago

Thanks @lehneres I'm going to try to implement something like this :)

simtechmedia commented 9 years ago

Maybe i need to revisit my approach, at the moment my server data is coming back after sending the router.dispatch

simtechmedia commented 9 years ago

Never got this to work :( If anyone has any pointers or examples of implementing fetching external JSON/JSONP data synchronously on load I would love the help

kpnigalye commented 8 years ago

@simtechmedia Not sure if you have got the solution for this but the following worked for me.

In router.js, I added my page route

on('/getfestival/:id', async (state) => {
    const festival = await http.get(`/api/festivals/festival/${state.params.id}`);
    return <FestivalPage {...festival} />;
  });

As you can see, I am making an API call inside. My API function gets record from database and returns response as follows:

import { Router } from 'express';
import festivals from '../server/models/festivalModel';

router.get('/festival/:id', async (req, res, next) => {
    festivalModel.find({_id:req.params.id}, (err, festival) => {
        if(err) throw err;
        res.send({festival});
    });
});

You can always add logs to your console to check the output. I always prefer adding logs.

Anyways, now my component should look like this

class FestivalPage extends Component{

    static contextTypes = {
        onSetTitle: PropTypes.func.isRequired,
    };

    render(){
        console.log(this.props);         // check your props here
        return (
          <div className="{s.root}">
            <div className="{s.container}">
                 <h1>{title}</h1>
                     <p><b>{ this.props.festival[0].name }</b></p>
             <p>{ this.props.festival[0].description }</p>
            </div>
          </div>
        );
    }
}

export default FestivalPage;

This worked for me. I hope this will work for you too.

simtechmedia commented 8 years ago

Thanks @kpnigalye ! I ended up doing much the same, just dumping it on the page and reading it through the props

ulani commented 3 years ago

@simtechmedia thank you very much for crating this issue! Unfortunately, we have close it due to inactivity. Feel free to re-open it or join our Discord channel for discussion.

NOTE: The main branch has been updated with React Starter Kit v2, using JAM-style architecture.