node.js - 如何避免使用 redux 在 react-apollo SSR 中重新获取客户端?

标签 node.js reactjs redux graphql react-apollo

我是使用 react-appollo 的 graphql 新手 我想将 react apollo 与 redux 以及服务器端渲染一起使用 一切都很好我的应用程序正在运行但问题是当我的应用程序渲染时它实际上再次调用了 api 它是不使用我的渲染状态..

enter image description here

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

商店.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;

首页.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(包括所有帖子)在返回给浏览器的 HTML 中。
  • React 然后将其更改为加载窗口
  • 然后 React 进行 API 调用,并呈现新帖子

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

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

此时,您可以在 Container 中移除对 GraphQL 的调用。您可以用“componentWillMount”的一些逻辑替换它。

逻辑是:

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

关于node.js - 如何避免使用 redux 在 react-apollo SSR 中重新获取客户端?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44670153/

相关文章:

reactjs - 如何避免使用useRef重新渲染 react ?

reactjs - 使用 UseEffect Hook 获取 API

reactjs - 无法在 redux-form-material-ui TextField 中设置 defaultValue

javascript - Action 不触发reducer

javascript - 使用动态生成的参数通过函数调用 Promise.all

javascript - Uncaught Error : Cannot find module 'jquery'

javascript - Typescript 运行时模块加载(使用 webpack)和类似 dll 的后期绑定(bind)

redux - 使用 Predicate 选项过滤 redux-logger 中的 redux-form 操作

node.js - 无法在 Ubuntu 中使用 Node 包管理器安装包

node.js - VSCode 永远不会打开正确的文件夹,而只是打开 System32