2017-06-21 72 views
6

Jestem nowy na graphql z react-appollo Chciałbym użyć funkcji apollo z redux również renderowanie po stronie serwera Wszystko jest w porządku, moja aplikacja działa, ale problem jest, gdy moja aplikacja renderuje, że faktycznie przypomina sobie api ponownie, to nie jest używając mojego świadczonych stan ..Jak uniknąć ponownego pobierania klienta w systemie operacyjnym-apollo SSR z redukcją?

enter image description here

serwer js

import express from 'express'; 
import bodyParser from 'body-parser'; 

import path from 'path'; 
import expressGraphQL from 'express-graphql'; 
import schema from './GraphQL/Schema'; 
import React from 'react'; 
import ReactDOMServer from 'react-dom/server' 
import { StaticRouter } from 'react-router'; 
import { ApolloClient, createNetworkInterface, ApolloProvider } from 'react-apollo'; 
import { getDataFromTree } from "react-apollo" 
import store from '../client/Redux/Store/store'; 

import {serverClient} from './lib/apollo' 

require('es6-promise').polyfill(); 
require('isomorphic-fetch'); 

import WApp from '../client/App'; 

//Dev HMR 
import HMR from './serverUtils/HMR'; 

const app = express(); 
app.use(bodyParser.json()); 

app.use('/api', expressGraphQL({ 
    schema, 
    graphiql: true 
})); 
app.use('/static',express.static('build')); 
HMR(app); 

function Html({ content, state }) { 
    return (
     <html> 
     <body> 
     <div id="app" dangerouslySetInnerHTML={{ __html: content }}/> 
     <script src="/static/app.js" /> 
     <script dangerouslySetInnerHTML={{ 
      __html: `window.__APOLLO_STATE__=${JSON.stringify(state).replace(/</g, '\\u003c')};`, 
     }} /> 
     </body> 
     </html> 
    ); 
} 

function createReactHandler(req) { 
    return async function reactHandler(ctx) { 
     const routeContext = {}; 
     const client = serverClient(); 

     const components = (
      <StaticRouter location={req.url} context={routeContext}> 
       <ApolloProvider store={store} client={client}> 
        <WApp /> 
       </ApolloProvider> 
      </StaticRouter> 
     ); 

     await getDataFromTree(components); 

     // const html = ReactDOMServer.renderToString(components); 

     // // Handle redirects 
     // if ([301, 302].includes(routeContext.status)) { 
     //  // 301 = permanent redirect, 302 = temporary 
     //  ctx.status = routeContext.status; 
     // 
     //  // Issue the new `Location:` header 
     //  ctx.redirect(routeContext.url); 
     // 
     //  // Return early -- no need to set a response body 
     //  return; 
     // } 
     // 
     // // Handle 404 Not Found 
     // if (routeContext.status === 404) { 
     //  // By default, just set the status code to 404. You can add your 
     //  // own custom logic here, if you want to redirect to a permanent 
     //  // 404 route or set a different response on `ctx.body` 
     //  ctx.status = routeContext.status; 
     // } 

     // return html; 
     // console.log(html) 


    } 
} 



const HTML = ({ html,state}) => (

    <html lang="en" prefix="og: http://ogp.me/ns#"> 
    <head> 
     <meta charSet="utf-8" /> 
     <meta httpEquiv="X-UA-Compatible" content="IE=edge" /> 
     <meta httpEquiv="Content-Language" content="en" /> 
     <meta name="viewport" content="width=device-width, initial-scale=1" /> 

    </head> 
    <body> 
    <div 
     id="app" 
     dangerouslySetInnerHTML={{ __html: html }} /> 
    <script dangerouslySetInnerHTML={{ 
     __html: `window.__STATE__=${JSON.stringify(state)};`, 
    }} /> 

    <script src="/static/app.js" /> 

    </body> 
    </html> 
); 

app.get('/*',(req,res) => { 
    const routeContext = {}; 
    const client = serverClient(); 

    const components = (
     <StaticRouter location={req.url} context={routeContext}> 
      <ApolloProvider store={store} client={client}> 
       <WApp /> 
      </ApolloProvider> 
     </StaticRouter> 
    ); 

    getDataFromTree(components).then(() => { 
     const html = ReactDOMServer.renderToString(components); 
     const initialState = {apollo: client.getInitialState()} 

     console.log(client); 

     res.send(`<!DOCTYPE html>\n${ReactDOMServer.renderToStaticMarkup(
      <HTML 
       html={html} 
       state={initialState} 
       />, 
     )}`) 
    }) 
}) 




app.listen(3000,() => { 
    console.log('Man I on') 
}) 

store.js

import { createStore, compose, applyMiddleware } from 'redux'; 
import { syncHistoryWithStore } from 'react-router-redux'; 
import thunk from 'redux-thunk'; 
import {createLogger} from 'redux-logger'; 


import client from '../apolloClient'; 
import rootReducer from '../Reducers' 

//All Reducer 
import {initialState as allPosts} from '../Reducers/AllPosts_Reucer'; 
const isProduction = process.env.NODE_ENV !== 'development'; 
const isClient = typeof document !== 'undefined'; 
const initialState = { 
    allPosts 
}; 

const middlewares = [thunk, client.middleware()]; 
const enhancers = []; 

if (!isProduction && isClient) { 
    const loggerMiddleware = createLogger(); 
    middlewares.push(loggerMiddleware); 

    if (typeof devToolsExtension === 'function') { 
     const devToolsExtension = window.devToolsExtension; 
     enhancers.push(devToolsExtension()); 
    } 
} 


const composedEnhancers = compose(
    applyMiddleware(...middlewares), 
    ...enhancers 
); 
const store = createStore(
    rootReducer, 
    {}, 

    composedEnhancers, 
); 

export default store; 

apolloClient.js

import ApolloClient, { 
    createNetworkInterface, 

} from 'apollo-client'; 
const isProduction = process.env.NODE_ENV !== 'development'; 
const testUrl = 'http://localhost:3000/api'; 

// const url = isProduction ? productionUrl : testUrl; 
const url = testUrl; 


const client = new ApolloClient({ 

    networkInterface: createNetworkInterface({uri:testUrl}), 
    dataIdFromObject:({id}) => id, 
    initialState: (typeof window !=='undefined')? window.__STATE__:{}, 
    reduxRootSelector:state => state.custom 

}); 

export default client; 

Home.js

import React,{Component} from 'react'; 
import { connect } from 'react-redux'; 
import { bindActionCreators } from 'redux'; 
import { graphql } from 'react-apollo'; 

import gql from 'graphql-tag'; 

import * as postActions from '../../Redux/Actions/postActions'; 


class Home extends Component{ 
    componentWillMount(){ 
     // console.log('From Will Mount',this.props.posts) 
    } 
    renderAllPost(){ 
     const {loading,posts} = this.props; 

     if(!loading){ 
      return posts.map(data => { 
       return <li key={data.id}>{data.title}</li> 
      }) 
     }else{ 
      return <div>loading</div> 
     } 
    } 
    render(){ 

     return(
      <div> 

       {this.renderAllPost()} 

      </div> 
     ) 
    } 
} 


//start from here 
const GetallPosts = gql` 
query getAllPosts{ 
    posts{ 
    id 
    title 
    body 
    } 
} 
`; 

const mapDispatchToProps = (dispatch) => ({ 
    actions:bindActionCreators(
     postActions, 
     dispatch 
    ) 
}); 


const ContainerWithData = graphql(GetallPosts,{ 
    props:({ data:{loading,posts} }) => ({ 
     posts, 
     loading, 
    }) 
})(Home) 


export default connect(
    // mapStateToPros, 
    // mapDispatchToProps 
)(ContainerWithData) 

Odpowiedz

1

Mogę potwierdzić, że rozumiem ten problem prawidłowo?

renderujesz stronę serwera HTML.

  • Kod HTML (wraz ze wszystkimi wpisami) znajduje się w kodzie HTML zwróconym do przeglądarki.
  • React następnie zmienia to do okna załadunku
  • React następnie sprawia, że ​​wywołanie API i generuje nowe posty

UWAGA: Apollo zawsze będzie wykonać połączenie AJAX, jak odbywa się to automatycznie jako część ContainerWithData.

Rozwiązanie Renderuj sklep Redux ze wszystkimi danymi. Na przykład podczas wykonywania połączenia z "createStore", obecnie przekazujesz pusty obiekt. Jeśli wykonasz połączenie AJAX tutaj, możesz zapełnić magazyn przeglądarki/redux wszystkimi wymaganymi danymi.

W tym miejscu można usunąć połączenie z GraphQL w kontenerze. Zastąpisz to pewną logiką za pomocą "componentWillMount".

Logika byłoby:

  • Tworzenie sklepu przy użyciu danych z modelu zwracane API
  • Call Home Komponent
  • główna biegnie "componentWillMount"
  • kontrole componentWillMount jeśli store.posts ma dane
    • następnie załaduj dane z API (GraphQL)
    • else return true i nadal renderowania
+0

mógłby Pan podać jakiś przykład Pls Kod .ill daje jakie dane chcesz. – Nane