React / Redux与.NET Core 2预渲染,每次路由更改为新组件时都会呈现新视图

时间:2017-10-04 15:50:19

标签: javascript asp.net .net reactjs asp.net-core-2.0

我有一个新的React / Redux应用程序,我最近移植到.NET Core 2.0 React / Redux模板。我使用了一些样板,但大部分都试图去除我不使用的东西。然而,我尝试使用的一件事是内置的服务器端预渲染。我遇到了正确呈现页面的问题,但是如果我单击一个导航按钮,该按钮应该将我带到另一个组件,它会调用页面控制器(在我的例子中是初始的HomeController)这是准系统),导致整个新视图呈现在服务器端。它仍然需要我使用正确的组件,但不是在完成回发之前。我觉得这可能是我想念的小事,但我似乎无法弄明白。

注意:

  • 如果我在boot-client.js中使用createHashHistory()而不是createBrowserHistory(),它将正确地路由到我的组件。但是,我认为从React -
  • 得到这个错误
  

尝试在容器中重用标记,但校验和无效。这通常意味着您正在使用服务器呈现,并且在服务器上生成的标记不是客户端所期望的。 React注入了新的标记以补偿哪些有效但你已经失去了服务器渲染的许多好处。相反,弄清楚为什么生成的标记在客户端或服务器上是不同的:

(client) eactid="4"><a href="#/" data-reactid="5"
 (server) eactid="4"><a href="/" data-reactid="5">

以下是我与之相关的一些文件:

引导client.js

&#13;
&#13;
import './css/site.css';
import 'bootstrap';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { HashRouter as Router, Switch, Link, Route, hashHistory, IndexRoute } from 'react-router-dom';
import { AppContainer } from 'react-hot-loader';
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'react-router-redux';
import { createBrowserHistory } from 'history';
import configureStore from './configureStore';
import { ApplicationState }  from './store';
import * as RoutesModule from './routes';
let routes = RoutesModule.routes;

import { createStore, applyMiddleware } from 'redux'; 
import reduxThunk from 'redux-thunk';
import reducers from './reducers';

// Create browser history to use in the Redux store
const baseUrl = document.getElementsByTagName('base')[0].getAttribute('href');
const history = createBrowserHistory({ baseName: '/' });

// Get the application-wide store instance, prepopulating with state from the server where available.
//const initialState = (window as any).initialReduxState as ApplicationState;
//const store = configureStore(history, initialState);

const createStoreWithMiddleware = applyMiddleware(reduxThunk)(createStore);  
const store = createStoreWithMiddleware(reducers);

function renderApp() {
    // This code starts up the React app when it runs in a browser. It sets up the routing configuration
    // and injects the app into a DOM element.
    ReactDOM.render(
        <AppContainer>
            <Provider store={ store }>
                <ConnectedRouter history={ history } children={ routes } />
            </Provider>
        </AppContainer>,
        document.getElementById('app')
    );
}

renderApp();

// Allow Hot Module Replacement
if (module.hot) {
    module.hot.accept('./routes', () => {
        routes = require('./routes').routes;
        renderApp();
    });
}
&#13;
&#13;
&#13;

引导server.js

&#13;
&#13;
import * as React from 'react';
import { Provider } from 'react-redux';
import { renderToString } from 'react-dom/server';
import { StaticRouter } from 'react-router-dom';
import { replace } from 'react-router-redux';
import { createMemoryHistory } from 'history';
import { createServerRenderer, RenderResult } from 'aspnet-prerendering';
import { routes } from './routes';
import configureStore from './configureStore';

import { createStore, applyMiddleware } from 'redux'; 
import reduxThunk from 'redux-thunk';
import reducers from './reducers/index.js';

export default createServerRenderer(params => {
    return new Promise((resolve, reject) => {
        // Prepare Redux store with in-memory history, and dispatch a navigation event
        // corresponding to the incoming URL
        const basename = params.baseUrl.substring(0, params.baseUrl.length - 1); // Remove trailing slash
        const urlAfterBasename = params.url.substring(basename.length);

 //const store = configureStore(createMemoryHistory());

        const createStoreWithMiddleware = applyMiddleware(reduxThunk)(createStore);  
        const store = createStoreWithMiddleware(reducers);

       // store.dispatch(replace(urlAfterBasename));

        // Prepare an instance of the application and perform an inital render that will
        // cause any async tasks (e.g., data access) to begin
        const routerContext = {};

        const app = (
            <Provider store={ store }>
                <StaticRouter basename={ basename } context={ routerContext } location={ params.location.path } children={ routes } />
            </Provider>
        );

        renderToString(app);

        // If there's a redirection, just send this information back to the host application
        if (routerContext.url) {
            resolve({ redirectUrl: routerContext.url });
            return;
        }
        
        // Once any async tasks are done, we can perform the final render
        // We also send the redux store state, so the client can continue execution where the server left off
        params.domainTasks.then(() => {
            resolve({
                html: renderToString(app),
                globals: { initialReduxState: store.getState() }
            });
        }, reject); // Also propagate any errors back into the host application
    });
});
&#13;
&#13;
&#13;

routes.jsx

&#13;
&#13;
export const routes = <Layout>
    <Route exact path='/' component={ StartPage } />
    <Route path='/somecomp1' component={ SomeComponent1 } />
    <Route exact path='/somecomp2' component={ SomeComponent2 } />
    <Route path='/somecomp3' component={ SomeComponent3 } />
</Layout>;
&#13;
&#13;
&#13;

Layout.jsx

&#13;
&#13;
export default class Layout extends React.Component {
    render() { return <div style={{ backgroundColor: '#F8F8FF' }}>
            <SomeHeaderComponent />
            <div id='content'>
               { this.props.children }                   
            </div>
        </div>
    }
}
&#13;
&#13;
&#13;

3 个答案:

答案 0 :(得分:0)

它无法解决实际问题,但从索引视图中删除asp-prerender-module Tag Helper会删除警告。

答案 1 :(得分:0)

我不确定你是否应该朝这个方向看,但是:

url的哈希部分和哈希更改不会发送到服务器。因此,服务器端呈现不能基于URL的散列部分执行任何操作,因此服务器不会创建具有服务器端呈现的HASH URL,因为当单击url / achor标记时,散列将永远不会导致再次调用服务器。

我建议您不要使用哈希值和/或深入了解这些预渲染包的源代码,以了解正在发生的事情。

其他猜测:当您点击href="/"网址时,即使在做出反应时,它会重新加载整个页面并转到:/因此这可能是没有任何反应的原因?尝试使用以下内容停止默认行为:event.preventDefault()

答案 2 :(得分:0)

使用Visual Studio模板中react-router-dom随附的NavLink代替anchor''标签来移动和导航应用程序

import { NavLink } from 'react-router-dom';

... class / constructor code ...

public render() {
    return <ul>
      <li>
          <NavLink to="/Students">Students</NavLink>
      </li>
    </ul>;
}

将其用于每个组件,以便react处理导航而不是您的Web服务器/控制器(启动应用程序重新呈现)