2016-03-26 4 views
7

ich eine static fetchData Methode auf meiner Route Komponente bin mit ...(Universal React + Redux + React-Router) Wie vermeidet man das erneute Abrufen von Routendaten beim ersten Laden des Browsers?

const mapStateToProps = (state) => ({ 
    posts: state.posts 
}) 

@connect(mapStateToProps) 
class Blog extends Component { 

    static fetchData (dispatch) { 
    return dispatch(fetchPosts()) 
    } 

    render() { 
    return (
     <PostsList posts={this.props.posts} /> 
    ) 
    } 

} 

... und Sammeln aller Versprechungen vor der ersten auf der Seite Server machen ...

match({ routes, location }, (error, redirectLocation, renderProps) => { 
    const promises = renderProps.components 
     .filter((component) => component.fetchData) 
     .map((component) => component.fetchData(store.dispatch)) 

    Promise.all(promises).then(() => { 
     res.status(200).send(renderView()) 
    }) 
}) 

Es funktioniert gut , der Server wartet, bis alle meine Versprechen vor dem Rendern der App gelöst sind.

nun auf meinem Client Skript, ich bin etwas ähnliches wie auf dem Server zu tun ...

... 
function resolveRoute (props) { 
    props.components 
    .filter((component) => component.fetchData) 
    .map((component) => component.fetchData(store.dispatch)) 

    return <RouterContext {...props} /> 
} 

render((
    <Provider store={store}> 
    <Router 
     history={browserHistory} 
     routes={routes} 
     render={resolveRoute} /> 
    </Provider> 
), document.querySelector('#app')) 

Und es funktioniert gut. Aber, wie Sie abziehen können, auf der ersten Seite Rendern wird die statische fetchData wird zweimal aufgerufen (einmal auf dem Server und einmal auf dem Client), und ich will das nicht.

Gibt es Vorschläge, wie Sie das lösen können? Empfehlungen?

Antwort

1

Sie können CanUseDOM von fbjs Modul verwenden.

import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment'; 
//only render on the server because it doesn't have DOM 
if(!canUseDOM) 
static fetch here 
+0

Aber mit diesem Weg würde ich nicht Server Seite Versprechen Auflösung verlieren? – kevinwolf

0

Eine bessere Idee ist Ihr Geschäft Zustand auf der Serverseite und Hydrat den anfänglichen Laden Zustand auf der Client-Seite mit dem dehydrierten Zustand zu entwässern.

Von Redux Doc:

Dies macht es einfach Universal-Anwendungen zu erstellen, wie der Zustand von Ihrem Server serialisiert und in den Client ohne zusätzlichen Programmieraufwand mit Feuchtigkeit versorgt werden kann.

http://redux.js.org/docs/introduction/ThreePrinciples.html

5

Ich schreibe dies von meinem Handy, so dass ich entschuldige mich für den Mangel an Formatierung.

Für mein Projekt mache ich etwas ähnliches wie Sie; Ich habe eine statische fetchData-Methode, ich durchlaufe die Komponenten von renderProps und dann rufe ich die statische Methode und warten auf die Versprechungen zu lösen.

Ich dann abrufen Zustand von meinem Redux speichern, stringify es und übergeben Sie es an meine Renderfunktion auf dem Server, so dass es ein initiales Statusobjekt auf dem Client rendern kann.

Vom Client nehme ich einfach diese Anfangszustandsvariable und gebe sie an meinen Reduxspeicher weiter. Redux wird dann dafür sorgen, dass Ihr Client-Speicher mit dem auf dem Server übereinstimmt. Von dort aus übergeben Sie Ihr Geschäft einfach an den Anbieter und gehen wie gewohnt weiter. Sie sollten Ihre statische Methode auf dem Client überhaupt nicht aufrufen müssen.

Für ein Beispiel von dem, was ich gesagt habe, können Sie mein Github-Projekt auschecken, wie Code selbst erklärt. https://github.com/mr-antivirus/riur

Hoffe, dass geholfen hat!


[Bearbeiten] Hier ist der Code!

Client.js

'use strict' 

import React from 'react'; 
import { render } from 'react-dom'; 
import { Provider } from 'react-redux'; 
import { Router, browserHistory } from 'react-router'; 
import createStore from '../shared/store/createStore'; 

import routes from '../shared/routes'; 

const store = createStore(window.__app_data); 
const history = browserHistory; 

render (
    <Provider store={store}> 
     <Router history={history} routes={routes} /> 
    </Provider>, 
    document.getElementById('content') 
) 

Server.js

app.use((req, res, next) => { 
    match({ routes, location:req.url }, (err, redirectLocation, renderProps) => { 
     if (err) { 
      return res.status(500).send(err); 
     } 

     if (redirectLocation) { 
      return res.redirect(302, redirectLocation.pathname + redirectLocation.search); 
     } 

     if (!renderProps) { 
      return next(); 
     } 

     // Create the redux store. 
     const store = createStore(); 

     // Retrieve the promises from React Router components that have a fetchData method. 
     // We use this data to populate our store for server side rendering. 
     const fetchedData = renderProps.components 
      .filter(component => component.fetchData) 
      .map(component => component.fetchData(store, renderProps.params)); 

     // Wait until ALL promises are successful before rendering. 
     Promise.all(fetchedData) 
      .then(() => { 
       const asset = { 
        javascript: { 
         main: '/js/bundle.js' 
        } 
       }; 

       const appContent = renderToString(
        <Provider store={store}> 
         <RouterContext {...renderProps} /> 
        </Provider> 
       ) 

       const isProd = process.env.NODE_ENV !== 'production' ? false : true; 

       res.send('<!doctype html>' + renderToStaticMarkup(<Html assets={asset} content={appContent} store={store} isProd={isProd} />)); 
      }) 
      .catch((err) => { 
       // TODO: Perform better error logging. 
       console.log(err); 
      }); 
    }); 
}); 

RedditContainer.js

class Reddit extends Component { 
    // Used by the server, ONLY, to fetch data 
    static fetchData(store) { 
     const { selectedSubreddit } = store.getState(); 
     return store.dispatch(fetchPosts(selectedSubreddit)); 
    } 

    // This will be called once on the client 
    componentDidMount() { 
     const { dispatch, selectedSubreddit } = this.props; 
     dispatch(fetchPostsIfNeeded(selectedSubreddit)); 
    } 

    ... Other methods 
}; 

HTML.js

Um es zu wiederholen ...

  1. auf dem Client die Zustandsvariablen greifen und es zu Ihrem Geschäft passieren.
  2. Führen Sie auf dem Server eine Schleife durch Ihre Komponenten, indem Sie fetchData aufrufen und Ihren Speicher übergeben. Warten Sie, bis die Versprechen gelöst sind, und rendern Sie.
  3. In HTML.js (Ihre RenderView-Funktion) serialisieren Sie Ihren Redux-Speicher und rendern die Ausgabe in eine Javascript-Variable für den Client.
  4. Erstellen Sie in Ihrer React-Komponente eine statische fetchData-Methode für ONLY den Server, der aufgerufen werden soll. Versenden Sie die Aktionen, die Sie benötigen.
+0

Danke! Ich hatte das gleiche Problem wie OP, aber Schritt # 4 war die ah-ha nur bei Bedarf auf mount für Client-Seite zu holen – franky

+0

Froh, dass es geholfen hat :-) –

Verwandte Themen