mirror of
https://github.com/aspnet/JavaScriptServices.git
synced 2025-12-22 17:47:53 +00:00
Add example of isomorphic cookies for #581
This commit is contained in:
@@ -8,6 +8,15 @@ import { syncHistoryWithStore } from 'react-router-redux';
|
|||||||
import routes from './routes';
|
import routes from './routes';
|
||||||
import configureStore from './configureStore';
|
import configureStore from './configureStore';
|
||||||
import { ApplicationState } from './store';
|
import { ApplicationState } from './store';
|
||||||
|
import cookie from 'react-cookie';
|
||||||
|
|
||||||
|
// If the server supplied any edits to cookies, apply them on the client
|
||||||
|
const cookieDataFromServer = window['cookieData'];
|
||||||
|
if (cookieDataFromServer) {
|
||||||
|
Object.getOwnPropertyNames(cookieDataFromServer).forEach(name => {
|
||||||
|
cookie.save(name, cookieDataFromServer[name]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Get the application-wide store instance, prepopulating with state from the server where available.
|
// Get the application-wide store instance, prepopulating with state from the server where available.
|
||||||
const initialState = (window as any).initialReduxState as ApplicationState;
|
const initialState = (window as any).initialReduxState as ApplicationState;
|
||||||
|
|||||||
@@ -6,9 +6,28 @@ import createMemoryHistory from 'history/lib/createMemoryHistory';
|
|||||||
import { createServerRenderer, RenderResult } from 'aspnet-prerendering';
|
import { createServerRenderer, RenderResult } from 'aspnet-prerendering';
|
||||||
import routes from './routes';
|
import routes from './routes';
|
||||||
import configureStore from './configureStore';
|
import configureStore from './configureStore';
|
||||||
|
import cookieUtil from 'cookie';
|
||||||
|
import cookie from 'react-cookie';
|
||||||
|
|
||||||
|
function plugInCookiesFromDotNet(cookieData: { key: string, value: string }[], res) {
|
||||||
|
const formattedData = {};
|
||||||
|
cookieData.forEach(keyValuePair => {
|
||||||
|
formattedData[keyValuePair.key] = keyValuePair.value;
|
||||||
|
});
|
||||||
|
cookie.plugToRequest({ cookies: formattedData }, res);
|
||||||
|
}
|
||||||
|
|
||||||
export default createServerRenderer(params => {
|
export default createServerRenderer(params => {
|
||||||
return new Promise<RenderResult>((resolve, reject) => {
|
return new Promise<RenderResult>((resolve, reject) => {
|
||||||
|
const cookiesModifiedOnServer = {};
|
||||||
|
if (params.data.cookies) {
|
||||||
|
// If we received some cookie data, use that to prepopulate 'react-cookie'
|
||||||
|
plugInCookiesFromDotNet(params.data.cookies, {
|
||||||
|
// Also track any cookies written on the server
|
||||||
|
cookie: (name, val) => { cookiesModifiedOnServer[name] = val; }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Match the incoming request against the list of client-side routes
|
// Match the incoming request against the list of client-side routes
|
||||||
match({ routes, location: params.location }, (error, redirectLocation, renderProps: any) => {
|
match({ routes, location: params.location }, (error, redirectLocation, renderProps: any) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
@@ -42,7 +61,17 @@ export default createServerRenderer(params => {
|
|||||||
params.domainTasks.then(() => {
|
params.domainTasks.then(() => {
|
||||||
resolve({
|
resolve({
|
||||||
html: renderToString(app),
|
html: renderToString(app),
|
||||||
globals: { initialReduxState: store.getState() }
|
globals: {
|
||||||
|
initialReduxState: store.getState(),
|
||||||
|
|
||||||
|
// Send any cookies written during server-side prerendering to the client.
|
||||||
|
// WARNING: Do not pass any security-sensitive cookies this way, because they will become
|
||||||
|
// readable in the HTML source. If your goal is to use this approach to manage authentication
|
||||||
|
// cookies, then be sure *not* to use 'globals' to send them to the client - instead, invoke
|
||||||
|
// Microsoft.AspNetCore.SpaServices.Prerendering.Prerender directly from your .NET code and
|
||||||
|
// only send the 'html' part back to the client (or at least, not all of the 'globals').
|
||||||
|
cookieData: cookiesModifiedOnServer
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}, reject); // Also propagate any errors back into the host application
|
}, reject); // Also propagate any errors back into the host application
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Action, Reducer, ThunkAction } from 'redux';
|
import { Action, Reducer, ThunkAction } from 'redux';
|
||||||
|
import cookie from 'react-cookie';
|
||||||
|
|
||||||
// -----------------
|
// -----------------
|
||||||
// STATE - This defines the type of data maintained in the Redux store.
|
// STATE - This defines the type of data maintained in the Redux store.
|
||||||
@@ -30,13 +31,20 @@ export const actionCreators = {
|
|||||||
|
|
||||||
// ----------------
|
// ----------------
|
||||||
// REDUCER - For a given state and action, returns the new state. To support time travel, this must not mutate the old state.
|
// REDUCER - For a given state and action, returns the new state. To support time travel, this must not mutate the old state.
|
||||||
|
const cookieKey = 'counterValue';
|
||||||
|
|
||||||
|
function modifiedCount(state: CounterState, delta: number): CounterState {
|
||||||
|
const newCount = state.count + delta;
|
||||||
|
cookie.save(cookieKey, newCount); // Ideally, don't do this here: have something that watches the store instead of having a side-effect from a reducer
|
||||||
|
return { count: newCount };
|
||||||
|
}
|
||||||
|
|
||||||
export const reducer: Reducer<CounterState> = (state: CounterState, action: KnownAction) => {
|
export const reducer: Reducer<CounterState> = (state: CounterState, action: KnownAction) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case 'INCREMENT_COUNT':
|
case 'INCREMENT_COUNT':
|
||||||
return { count: state.count + 1 };
|
return modifiedCount(state, +1);
|
||||||
case 'DECREMENT_COUNT':
|
case 'DECREMENT_COUNT':
|
||||||
return { count: state.count - 1 };
|
return modifiedCount(state, -1);
|
||||||
default:
|
default:
|
||||||
// The following line guarantees that every action in the KnownAction union has been covered by a case above
|
// The following line guarantees that every action in the KnownAction union has been covered by a case above
|
||||||
const exhaustiveCheck: never = action;
|
const exhaustiveCheck: never = action;
|
||||||
@@ -44,5 +52,6 @@ export const reducer: Reducer<CounterState> = (state: CounterState, action: Know
|
|||||||
|
|
||||||
// For unrecognized actions (or in cases where actions have no effect), must return the existing state
|
// For unrecognized actions (or in cases where actions have no effect), must return the existing state
|
||||||
// (or default initial state if none was supplied)
|
// (or default initial state if none was supplied)
|
||||||
return state || { count: 0 };
|
const prevCountFromCookie = parseInt(cookie.load(cookieKey) || '0');
|
||||||
|
return state || { count: prevCountFromCookie };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
ViewData["Title"] = "Home Page";
|
ViewData["Title"] = "Home Page";
|
||||||
}
|
}
|
||||||
|
|
||||||
<div id="react-app" asp-prerender-module="ClientApp/dist/main-server">Loading...</div>
|
<div id="react-app" asp-prerender-module="ClientApp/dist/main-server"
|
||||||
|
asp-prerender-data="new { cookies = ViewContext.HttpContext.Request.Cookies }">Loading...</div>
|
||||||
|
|
||||||
@section scripts {
|
@section scripts {
|
||||||
<script src="~/dist/main-client.js" asp-append-version="true"></script>
|
<script src="~/dist/main-client.js" asp-append-version="true"></script>
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
"json-loader": "^0.5.4",
|
"json-loader": "^0.5.4",
|
||||||
"node-noop": "^1.0.0",
|
"node-noop": "^1.0.0",
|
||||||
"react": "^15.3.2",
|
"react": "^15.3.2",
|
||||||
|
"react-cookie": "^1.0.4",
|
||||||
"react-dom": "^15.3.2",
|
"react-dom": "^15.3.2",
|
||||||
"react-redux": "^4.4.5",
|
"react-redux": "^4.4.5",
|
||||||
"react-router": "^2.8.1",
|
"react-router": "^2.8.1",
|
||||||
|
|||||||
Reference in New Issue
Block a user