如何避免客户端在rea-apollo SSR中重新获取?

我是新的graphql与react-appollo我想用reax apollo与redux也服务器端呈现每一件事情都很好我的应用程序正在工作,但问题是当我的应用程序渲染它实际上是再次调用API它不使用我呈现状态

在这里输入图像描述

服务器.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) 

我能否确认我正确理解问题?

您正在呈现HTML服务器端。

  • HTML(包括所有的post)在HTML中返回给浏览器。
  • React然后将其更改为加载窗口
  • 然后React进行API调用,并呈现新post

注:Apollo将始终进行AJAX调用,因为这是作为ContainerWithData的一部分自动完成的。

解决scheme使用所有数据渲染Redux Store。 例如,在调用“createStore”时,当前正在传入一个空对象。 如果您在此处进行AJAX调用,则可以使用所需的所有数据填充浏览器/ restorex存储。

此时,您可以在Container中删除对GraphQL的调用。 你可以用“componentWillMount”replace这个逻辑。

逻辑是:

  • 使用从API返回的模型数据创build商店
  • 呼叫家庭组件
  • 本垒打“componentWillMount”
  • componentWillMount检查store.posts是否有数据
    • 然后从API(GraphQL)加载数据
    • 否则返回true并继续渲染