next-redux-wrapper
Advanced tools
Comparing version 8.1.0 to 9.0.0-rc.1
/// <reference types="node" /> | ||
/// <reference types="react" /> | ||
import App, { AppContext, AppInitialProps } from 'next/app'; | ||
import { Store } from 'redux'; | ||
import { AppContext } from 'next/app'; | ||
import { Store, Middleware, Action } from 'redux'; | ||
import { GetServerSideProps, GetServerSidePropsContext, GetStaticProps, GetStaticPropsContext, NextComponentType, NextPageContext } from 'next'; | ||
/** | ||
* Quick note on Next.js return types: | ||
* | ||
* Page.getInitialProps https://nextjs.org/docs/api-reference/data-fetching/getInitialProps | ||
* as-is | ||
* | ||
* App.getInitialProps: AppInitialProps https://nextjs.org/docs/advanced-features/custom-app | ||
* {pageProps: any} | ||
* | ||
* getStaticProps https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation | ||
* {props: any} | ||
* | ||
* getServerSideProps https://nextjs.org/docs/basic-features/data-fetching#getserversideprops-server-side-rendering | ||
* {props: any} | ||
*/ | ||
export declare const HYDRATE = "__NEXT_REDUX_WRAPPER_HYDRATE__"; | ||
export declare type MakeStore<S extends Store> = (context: Context) => S; | ||
export declare type MakeStore<S extends Store> = (init: { | ||
context: Context; | ||
reduxWrapperMiddleware: Middleware; | ||
}) => S; | ||
export interface InitStoreOptions<S extends Store> { | ||
makeStore: MakeStore<S>; | ||
context?: Context; | ||
config: Config<S>; | ||
} | ||
export declare enum Source { | ||
GIAP = "GIAP", | ||
GIPP = "GIPP", | ||
GSP = "GSP", | ||
GSSP = "GSSP" | ||
} | ||
export declare const createWrapper: <S extends Store<any, import("redux").AnyAction>>(makeStore: MakeStore<S>, config?: Config<S>) => { | ||
getServerSideProps: <P extends {} = any>(callback: GetServerSidePropsCallback<S, P>) => GetServerSideProps<P, import("querystring").ParsedUrlQuery, import("next").PreviewData>; | ||
getStaticProps: <P_1 extends {} = any>(callback: GetStaticPropsCallback<S, P_1>) => GetStaticProps<P_1, import("querystring").ParsedUrlQuery, import("next").PreviewData>; | ||
getInitialAppProps: <P_2 extends {} = any>(callback: AppCallback<S, P_2>) => GetInitialAppProps<P_2>; | ||
getInitialPageProps: <P_3 extends {} = any>(callback: PageCallback<S, P_3>) => ((context: NextPageContext<any>) => any) | undefined; | ||
withRedux: (Component: NextComponentType | App | any) => { | ||
(props: any): JSX.Element; | ||
displayName: string; | ||
getInitialProps: any; | ||
getInitialAppProps: <P_2 extends {} = any>(callback: AppCallback<S, P_2>) => ({ Component, ctx }: AppContext) => Promise<{ | ||
pageProps: P_2; | ||
}> | { | ||
pageProps: P_2; | ||
}; | ||
useWrappedStore: ({ initialState: giapState, initialProps, ...props }: any, displayName?: string) => { | ||
store: S; | ||
props: any; | ||
getInitialPageProps: <P_3 extends {} = any>(callback: PageCallback<S, P_3>) => ((context: NextPageContext) => P_3 | Promise<P_3>) | undefined; | ||
useStore: () => S; | ||
useHydration: ({ reduxWrapperActionsGSSP, reduxWrapperActionsGSP, reduxWrapperActionsGIAP, reduxWrapperActionsGIPP, }: PageProps | any) => { | ||
hydrating: boolean; | ||
}; | ||
}; | ||
declare const _default: <S extends Store<any, import("redux").AnyAction>>(makeStore: MakeStore<S>, config?: Config<S>) => (Component: any) => { | ||
(props: any): JSX.Element; | ||
displayName: string; | ||
getInitialProps: any; | ||
}; | ||
export default _default; | ||
export declare type Context = NextPageContext | AppContext | GetStaticPropsContext | GetServerSidePropsContext; | ||
export interface Config<S extends Store> { | ||
serializeState?: (state: ReturnType<S['getState']>) => any; | ||
deserializeState?: (state: any) => ReturnType<S['getState']>; | ||
serializeAction?: (state: ReturnType<S['getState']>) => any; | ||
deserializeAction?: (state: any) => ReturnType<S['getState']>; | ||
debug?: boolean; | ||
actionFilter?: (action: Action) => boolean | any; | ||
} | ||
export interface WrapperProps { | ||
initialProps: any; | ||
initialState: any; | ||
export interface PageProps { | ||
reduxWrapperActionsGIAP?: Action[]; | ||
reduxWrapperActionsGIPP?: Action[]; | ||
reduxWrapperActionsGSSP?: Action[]; | ||
reduxWrapperActionsGSP?: Action[]; | ||
} | ||
declare type GetInitialPageProps<P> = NextComponentType<NextPageContext, any, P>['getInitialProps']; | ||
declare type GetInitialAppProps<P> = ({ Component, ctx }: AppContext) => Promise<AppInitialProps & { | ||
pageProps: P; | ||
}>; | ||
export interface WrapperProps<P extends Object> { | ||
reduxWrapperActions: Action[]; | ||
initialProps: P; | ||
} | ||
export declare type GetStaticPropsCallback<S extends Store, P extends { | ||
@@ -68,14 +57,10 @@ [key: string]: any; | ||
}> = (store: S) => GetServerSideProps<P>; | ||
export declare type PageCallback<S extends Store, P> = (store: S) => GetInitialPageProps<P>; | ||
export declare type AppCallback<S extends Store, P> = (store: S) => GetInitialAppProps<P>; | ||
export declare type PageCallback<S extends Store, P> = (store: S) => NextComponentType<NextPageContext, P, P>['getInitialProps']; | ||
export declare type AppCallback<S extends Store, P> = (store: S) => ({ Component, ctx }: AppContext) => Promise<{ | ||
pageProps: P; | ||
}> | { | ||
pageProps: P; | ||
}; | ||
export declare type Callback<S extends Store, P extends { | ||
[key: string]: any; | ||
}> = GetStaticPropsCallback<S, P> | GetServerSidePropsCallback<S, P> | PageCallback<S, P> | AppCallback<S, P>; | ||
declare module 'next' { | ||
interface NextPageContext<S extends Store = any> { | ||
/** | ||
* Provided by next-redux-wrapper: The redux store | ||
*/ | ||
store: S; | ||
} | ||
} |
441
lib/index.js
@@ -13,21 +13,2 @@ "use strict"; | ||
}; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
}) : function(o, v) { | ||
o["default"] = v; | ||
}); | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); | ||
__setModuleDefault(result, mod); | ||
return result; | ||
}; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
@@ -69,18 +50,15 @@ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
}; | ||
var __rest = (this && this.__rest) || function (s, e) { | ||
var t = {}; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) | ||
t[p] = s[p]; | ||
if (s != null && typeof Object.getOwnPropertySymbols === "function") | ||
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { | ||
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) | ||
t[p[i]] = s[p[i]]; | ||
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { | ||
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { | ||
if (ar || !(i in from)) { | ||
if (!ar) ar = Array.prototype.slice.call(from, 0, i); | ||
ar[i] = from[i]; | ||
} | ||
return t; | ||
} | ||
return to.concat(ar || Array.prototype.slice.call(from)); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.createWrapper = exports.HYDRATE = void 0; | ||
var react_1 = __importStar(require("react")); | ||
exports.createWrapper = exports.Source = void 0; | ||
var react_1 = require("react"); | ||
var react_redux_1 = require("react-redux"); | ||
var router_1 = require("next/router"); | ||
/** | ||
@@ -101,17 +79,48 @@ * Quick note on Next.js return types: | ||
*/ | ||
exports.HYDRATE = '__NEXT_REDUX_WRAPPER_HYDRATE__'; | ||
var RENDER = '__NEXT_REDUX_WRAPPER_FIRST_RENDER__'; | ||
var REQPROP = '__NEXT_REDUX_WRAPPER_STORE__'; | ||
var getIsServer = function () { return typeof window === 'undefined'; }; | ||
var getDeserializedState = function (initialState, _a) { | ||
var _b = _a === void 0 ? {} : _a, deserializeState = _b.deserializeState; | ||
return deserializeState ? deserializeState(initialState) : initialState; | ||
var getDeserializedAction = function (action, _a) { | ||
var _b = _a === void 0 ? {} : _a, deserializeAction = _b.deserializeAction; | ||
return deserializeAction ? deserializeAction(action) : action; | ||
}; | ||
var getSerializedState = function (state, _a) { | ||
var _b = _a === void 0 ? {} : _a, serializeState = _b.serializeState; | ||
return serializeState ? serializeState(state) : state; | ||
var getSerializedAction = function (action, _a) { | ||
var _b = _a === void 0 ? {} : _a, serializeAction = _b.serializeAction; | ||
return (serializeAction ? serializeAction(action) : action); | ||
}; | ||
var getActionFilter = function (_a) { | ||
var _b = _a === void 0 ? {} : _a, actionFilter = _b.actionFilter; | ||
return function (action) { | ||
return actionFilter ? actionFilter(action) : true; | ||
}; | ||
}; | ||
var Source; | ||
(function (Source) { | ||
Source["GIAP"] = "GIAP"; | ||
Source["GIPP"] = "GIPP"; | ||
Source["GSP"] = "GSP"; | ||
Source["GSSP"] = "GSSP"; | ||
})(Source = exports.Source || (exports.Source = {})); | ||
var sharedClientStore; | ||
var undefinedReplacer = function (key, value) { return (typeof value === 'undefined' ? null : value); }; | ||
var createMiddleware = function (config) { | ||
var log = []; | ||
var reduxWrapperMiddleware = function (api) { return function (next) { return function (action) { | ||
if (!getIsServer()) { | ||
return next(action); | ||
} | ||
action = JSON.parse(JSON.stringify(action, undefinedReplacer)); | ||
log.push(getSerializedAction(action, config)); | ||
return next(action); | ||
}; }; }; | ||
return { log: log, reduxWrapperMiddleware: reduxWrapperMiddleware }; | ||
}; | ||
var initStore = function (_a) { | ||
var _b, _c, _d; | ||
var makeStore = _a.makeStore, _e = _a.context, context = _e === void 0 ? {} : _e; | ||
var createStore = function () { return makeStore(context); }; | ||
var makeStore = _a.makeStore, _e = _a.context, context = _e === void 0 ? {} : _e, config = _a.config; | ||
var createStore = function () { | ||
var _a = createMiddleware(config), log = _a.log, reduxWrapperMiddleware = _a.reduxWrapperMiddleware; | ||
var store = makeStore({ context: context, reduxWrapperMiddleware: reduxWrapperMiddleware }); | ||
return { store: store, log: log }; | ||
}; | ||
if (getIsServer()) { | ||
@@ -122,11 +131,23 @@ var req = ((_b = context) === null || _b === void 0 ? void 0 : _b.req) || ((_d = (_c = context) === null || _c === void 0 ? void 0 : _c.ctx) === null || _d === void 0 ? void 0 : _d.req); | ||
// @see https://github.com/kirill-konshin/next-redux-wrapper/pull/196#issuecomment-611673546 | ||
if (!req.__nextReduxWrapperStore) { | ||
req.__nextReduxWrapperStore = createStore(); // Used in GIP/GSSP | ||
if (!req[REQPROP]) { | ||
if (config.debug) { | ||
console.log('1. Store created within request'); | ||
} | ||
req[REQPROP] = createStore(); // GIP/GSSP | ||
} | ||
return req.__nextReduxWrapperStore; | ||
else if (config.debug) { | ||
console.log('1. Store reused from request'); | ||
} | ||
return req[REQPROP]; | ||
} | ||
return createStore(); | ||
if (config.debug) { | ||
console.log('1. Store created without request'); | ||
} | ||
return createStore(); // GSP or server rendering | ||
} | ||
// Memoize the store if we're on the client | ||
if (!sharedClientStore) { | ||
if (config.debug) { | ||
console.log('1. Store created on client'); | ||
} | ||
sharedClientStore = createStore(); | ||
@@ -136,24 +157,76 @@ } | ||
}; | ||
/** | ||
* Returns an array of tuples with state and source. An array is needed to guarantee the order. | ||
* Returns an empty array if it does not recognize state | ||
* | ||
* If GSP has run, then state will _not_ contain the data from GIAP (if it exists), because GSP is run at build | ||
* time, and GIAP runs at request time. | ||
* | ||
* So we have to hydrate the GSP data first, and then do another hydrate on the GIAP state. | ||
* | ||
* If GSSP has run, then state _will_ contain the data from GIAP (if there is a GIAP) and the GSSP data combined | ||
* (see https://github.com/kirill-konshin/next-redux-wrapper/pull/499#discussion_r1014500941), thus one hydrate. | ||
* !!! Not applied anymore since we flush log after dispatches. | ||
* | ||
* If there is no GSP or GSSP for this page, but there is a GIPP (not _app), there will be one hydrate. | ||
* | ||
* If there is no GSP or GSSP and no GIP on page level for this page, but there is a GIAP on _app level there | ||
* will be also one hydrate. | ||
* | ||
* GIPP (partial) -> GIAP (full) | ||
* GIAP (partial) -> GSSP (full) | ||
* GIAP (partial) -> GSP (partial) | ||
*/ | ||
var getStates = function (_a) { | ||
var reduxWrapperActionsGSSP = _a.reduxWrapperActionsGSSP, reduxWrapperActionsGSP = _a.reduxWrapperActionsGSP, reduxWrapperActionsGIAP = _a.reduxWrapperActionsGIAP, reduxWrapperActionsGIPP = _a.reduxWrapperActionsGIPP; | ||
var map = new Map(); | ||
if (reduxWrapperActionsGIAP) { | ||
if (reduxWrapperActionsGIPP) { | ||
// send both as they both are partial, order is important as GIPP happens before GIAP | ||
map.set(Source.GIPP, reduxWrapperActionsGIPP); | ||
map.set(Source.GIAP, reduxWrapperActionsGIAP); | ||
} | ||
else if (reduxWrapperActionsGSSP) { | ||
// send both as they both are partial, order is important as GIAP happens before GSSP | ||
map.set(Source.GIAP, reduxWrapperActionsGIAP); | ||
map.set(Source.GSSP, reduxWrapperActionsGSSP); | ||
} | ||
else if (reduxWrapperActionsGSP) { | ||
// send both as they both are partial, order is important as GSP happens way before GIAP | ||
map.set(Source.GSP, reduxWrapperActionsGSP); | ||
map.set(Source.GIAP, reduxWrapperActionsGIAP); | ||
} | ||
else { | ||
map.set(Source.GIAP, reduxWrapperActionsGIAP); // simply return GIAP | ||
} | ||
} | ||
else if (reduxWrapperActionsGSP) { | ||
map.set(Source.GSP, reduxWrapperActionsGSP); | ||
} | ||
else if (reduxWrapperActionsGSSP) { | ||
map.set(Source.GSSP, reduxWrapperActionsGSSP); | ||
} | ||
else if (reduxWrapperActionsGIPP) { | ||
map.set(Source.GIPP, reduxWrapperActionsGIPP); | ||
} | ||
return map; | ||
}; | ||
var dispatchStates = function (dispatch, states, config) { | ||
return getStates(states).forEach(function (actions, source) { | ||
return actions.filter(getActionFilter(config)).forEach(function (action) { | ||
action = getDeserializedAction(action, config); | ||
dispatch(__assign(__assign({}, action), { meta: __assign(__assign({}, action.meta), { source: source }) })); | ||
}); | ||
}); | ||
}; | ||
var createWrapper = function (makeStore, config) { | ||
if (config === void 0) { config = {}; } | ||
var makeProps = function (_a) { | ||
var callback = _a.callback, context = _a.context, _b = _a.addStoreToContext, addStoreToContext = _b === void 0 ? false : _b; | ||
return __awaiter(void 0, void 0, void 0, function () { | ||
var store, nextCallback, initialProps, _c, state; | ||
var callback = _a.callback, context = _a.context; | ||
return __awaiter(this, void 0, void 0, function () { | ||
var _b, store, log, nextCallback, initialProps, _c, reduxWrapperActions; | ||
return __generator(this, function (_d) { | ||
switch (_d.label) { | ||
case 0: | ||
store = initStore({ context: context, makeStore: makeStore }); | ||
if (config.debug) { | ||
console.log("1. getProps created store with state", store.getState()); | ||
} | ||
// Legacy stuff - put store in context | ||
if (addStoreToContext) { | ||
if (context.ctx) { | ||
context.ctx.store = store; | ||
} | ||
else { | ||
context.store = store; | ||
} | ||
} | ||
_b = initStore({ context: context, makeStore: makeStore, config: config }), store = _b.store, log = _b.log; | ||
nextCallback = callback && callback(store); | ||
@@ -167,10 +240,11 @@ _c = nextCallback; | ||
case 2: | ||
initialProps = (_c) || {}; | ||
initialProps = ((_c) || {}); | ||
if (config.debug) { | ||
console.log("3. getProps after dispatches has store state", store.getState()); | ||
console.log("2. initial state after dispatches", store.getState()); | ||
} | ||
state = store.getState(); | ||
reduxWrapperActions = __spreadArray([], log, true); | ||
log.splice(0, log.length); // flush all logs | ||
return [2 /*return*/, { | ||
initialProps: initialProps, | ||
initialState: getIsServer() ? getSerializedState(state, config) : state, | ||
reduxWrapperActions: reduxWrapperActions, | ||
}]; | ||
@@ -183,11 +257,13 @@ } | ||
return function (context) { return __awaiter(void 0, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
var _a, reduxWrapperActions, initialProps; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: | ||
// context is store — avoid double-wrapping | ||
if ('getState' in context) { | ||
return [2 /*return*/, callback && callback(context)]; | ||
if (!context.query || !context.pathname || !context.AppTree) { | ||
throw new Error("Looks like you've used getInitialPageProps for different kind of lifecycle method"); | ||
} | ||
return [4 /*yield*/, makeProps({ callback: callback, context: context, addStoreToContext: true })]; | ||
case 1: return [2 /*return*/, _a.sent()]; | ||
return [4 /*yield*/, makeProps({ callback: callback, context: context })]; | ||
case 1: | ||
_a = _b.sent(), reduxWrapperActions = _a.reduxWrapperActions, initialProps = _a.initialProps; | ||
return [2 /*return*/, __assign(__assign({}, initialProps), { reduxWrapperActionsGIPP: reduxWrapperActions })]; | ||
} | ||
@@ -199,9 +275,13 @@ }); | ||
return function (context) { return __awaiter(void 0, void 0, void 0, function () { | ||
var _a, initialProps, initialState; | ||
var _a, reduxWrapperActions, initialProps; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: return [4 /*yield*/, makeProps({ callback: callback, context: context, addStoreToContext: true })]; | ||
case 0: | ||
if (!context.router || !context.Component || !context.AppTree || !context.ctx) { | ||
throw new Error("Looks like you've used getInitialAppProps for different kind of lifecycle method"); | ||
} | ||
return [4 /*yield*/, makeProps({ callback: callback, context: context })]; | ||
case 1: | ||
_a = _b.sent(), initialProps = _a.initialProps, initialState = _a.initialState; | ||
return [2 /*return*/, __assign(__assign({}, initialProps), { initialState: initialState })]; | ||
_a = _b.sent(), reduxWrapperActions = _a.reduxWrapperActions, initialProps = _a.initialProps; | ||
return [2 /*return*/, __assign(__assign({}, initialProps), { pageProps: __assign(__assign({}, initialProps.pageProps), { reduxWrapperActionsGIAP: reduxWrapperActions }) })]; | ||
} | ||
@@ -213,3 +293,3 @@ }); | ||
return function (context) { return __awaiter(void 0, void 0, void 0, function () { | ||
var _a, initialProps, initialState; | ||
var _a, reduxWrapperActions, initialProps; | ||
return __generator(this, function (_b) { | ||
@@ -219,4 +299,4 @@ switch (_b.label) { | ||
case 1: | ||
_a = _b.sent(), initialProps = _a.initialProps, initialState = _a.initialState; | ||
return [2 /*return*/, __assign(__assign({}, initialProps), { props: __assign(__assign({}, initialProps.props), { initialState: initialState }) })]; | ||
_a = _b.sent(), reduxWrapperActions = _a.reduxWrapperActions, initialProps = _a.initialProps; | ||
return [2 /*return*/, __assign(__assign({}, initialProps), { props: __assign(__assign({}, initialProps.props), { reduxWrapperActionsGSP: reduxWrapperActions }) })]; | ||
} | ||
@@ -227,116 +307,94 @@ }); | ||
var getServerSideProps = function (callback) { | ||
return function (context) { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, getStaticProps(callback)(context)]; | ||
case 1: return [2 /*return*/, _a.sent()]; | ||
return function (context) { return __awaiter(void 0, void 0, void 0, function () { | ||
var _a, reduxWrapperActions, initialProps; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: | ||
if (!context.req || !context.res || !context.resolvedUrl || !context.query) { | ||
throw new Error("Looks like you've used getServerSideProps for different kind of lifecycle method"); | ||
} | ||
return [4 /*yield*/, makeProps({ callback: callback, context: context })]; | ||
case 1: | ||
_a = _b.sent(), reduxWrapperActions = _a.reduxWrapperActions, initialProps = _a.initialProps; | ||
return [2 /*return*/, __assign(__assign({}, initialProps), { props: __assign(__assign({}, initialProps.props), { reduxWrapperActionsGSSP: reduxWrapperActions }) })]; | ||
} | ||
}); | ||
}); }; | ||
}; | ||
var useStore = function () { return (0, react_1.useMemo)(function () { return initStore({ makeStore: makeStore, config: config }).store; }, []); }; | ||
var useHydration = function (_a) { | ||
var reduxWrapperActionsGSSP = _a.reduxWrapperActionsGSSP, reduxWrapperActionsGSP = _a.reduxWrapperActionsGSP, reduxWrapperActionsGIAP = _a.reduxWrapperActionsGIAP, reduxWrapperActionsGIPP = _a.reduxWrapperActionsGIPP; | ||
var dispatch = (0, react_redux_1.useDispatch)(); | ||
var _b = (0, react_1.useState)(true), hydrating = _b[0], setHydrating = _b[1]; | ||
var hydrate = (0, react_1.useCallback)(function () { | ||
return dispatchStates(dispatch, { | ||
reduxWrapperActionsGSSP: reduxWrapperActionsGSSP, | ||
reduxWrapperActionsGSP: reduxWrapperActionsGSP, | ||
reduxWrapperActionsGIAP: reduxWrapperActionsGIAP, | ||
reduxWrapperActionsGIPP: reduxWrapperActionsGIPP, | ||
}, config); | ||
}, [dispatch, reduxWrapperActionsGSSP, reduxWrapperActionsGSP, reduxWrapperActionsGIAP, reduxWrapperActionsGIPP]); | ||
// This guard is solely to suppress Next.js warning about useless layout effect | ||
// Coverage is not gathered, all checks are via demo apps | ||
/* c8 ignore start */ | ||
if (!getIsServer()) { | ||
// eslint-disable-next-line react-hooks/rules-of-hooks | ||
(0, react_1.useLayoutEffect)(function () { | ||
if (!window[RENDER]) { | ||
window[RENDER] = true; | ||
return; | ||
} | ||
// if there are only GIAP or GIPP these actions were already dispatched on client side | ||
if (!reduxWrapperActionsGSP && !reduxWrapperActionsGSSP) { | ||
if (config.debug) { | ||
console.log('4. useHydration effect skipped'); | ||
} | ||
return; | ||
} | ||
setHydrating(true); | ||
if (config.debug) { | ||
console.log('3. useHydration effect'); | ||
} | ||
hydrate(); | ||
setHydrating(false); | ||
if (config.debug) { | ||
console.log('4. useHydration done'); | ||
} | ||
}, [dispatch, hydrate, reduxWrapperActionsGSP, reduxWrapperActionsGSSP]); | ||
if (window[RENDER]) { | ||
return { hydrating: hydrating }; | ||
} | ||
}); }); }; | ||
}; // just not to repeat myself | ||
var hydrate = function (store, state) { | ||
if (!state) { | ||
return; | ||
} | ||
store.dispatch({ | ||
type: exports.HYDRATE, | ||
payload: getDeserializedState(state, config), | ||
}); | ||
}; | ||
var hydrateOrchestrator = function (store, giapState, gspState, gsspState, gippState) { | ||
var _a; | ||
if (gspState) { | ||
// If GSP has run, then gspState will _not_ contain the data from GIP (if it exists), because GSP is run at build time, | ||
// and GIP runs at request time. So we have to hydrate the GIP data first, and then do another hydrate on the gspState. | ||
hydrate(store, giapState); | ||
hydrate(store, gspState); | ||
/*c8 ignore end */ | ||
/** | ||
* When we navigate client side, we may always synchronously hydrate the state before the new page components | ||
* are mounted. This means we hydrate while the previous page components are still mounted. | ||
* | ||
* This will cause ugly react message, that you're trying to update state of a component while rendering another. | ||
* Warnings only appear in development mode. | ||
* | ||
* You might think that might cause issues because the selectors on the previous page (still mounted) will suddenly | ||
* contain other data, and maybe even nested properties, causing null reference exceptions. | ||
* | ||
* But that's not the case. | ||
* | ||
* Hydrating synchronously will not trigger a rerender of the still mounted page component. So if your selectors do | ||
* have some initial state values causing them to rerun after hydration, and you're accessing deeply nested values | ||
* inside your components, you still wouldn't get errors, because there's no rerender. | ||
* | ||
* Instead, React will render the new page components straight away, which will have selectors with the correct data. | ||
* | ||
* So technically, other than an ugly warning, this does not have consequences. Nevertheless, subsequent navigation | ||
* events (without reload) will be dispatched from useEffect, properly. This means that for split second selectors | ||
* may render empty data. React may batch state updates though (or not). | ||
*/ | ||
if (config.debug) { | ||
console.log('3. useHydration sync'); | ||
} | ||
else if (gsspState || gippState || giapState) { | ||
// If GSSP has run, then gsspState _will_ contain the data from GIP (if there is a GIP) and the GSSP data combined | ||
// (see https://github.com/kirill-konshin/next-redux-wrapper/pull/499#discussion_r1014500941). | ||
// If there is no GSP or GSSP for this page, but there is a GIP on page level (not _app), then we use the gippState. | ||
// If there is no GSP or GSSP and no GIP on page level for this page, but there is a GIP on _app level, then we use the giapState. | ||
hydrate(store, (_a = gsspState !== null && gsspState !== void 0 ? gsspState : gippState) !== null && _a !== void 0 ? _a : giapState); | ||
} | ||
}; | ||
var useHybridHydrate = function (store, giapState, gspState, gsspState, gippState) { | ||
var events = (0, router_1.useRouter)().events; | ||
var shouldHydrate = (0, react_1.useRef)(true); | ||
// We should only hydrate when the router has changed routes | ||
(0, react_1.useEffect)(function () { | ||
var handleStart = function () { | ||
shouldHydrate.current = true; | ||
}; | ||
events === null || events === void 0 ? void 0 : events.on('routeChangeStart', handleStart); | ||
return function () { | ||
events === null || events === void 0 ? void 0 : events.off('routeChangeStart', handleStart); | ||
}; | ||
}, [events]); | ||
// useMemo so that when we navigate client side, we always synchronously hydrate the state before the new page | ||
// components are mounted. This means we hydrate while the previous page components are still mounted. | ||
// You might think that might cause issues because the selectors on the previous page (still mounted) will suddenly | ||
// contain other data, and maybe even nested properties, causing null reference exceptions. | ||
// But that's not the case. | ||
// Hydrating in useMemo will not trigger a rerender of the still mounted page component. So if your selectors do have | ||
// some initial state values causing them to rerun after hydration, and you're accessing deeply nested values inside your | ||
// components, you still wouldn't get errors, because there's no rerender. | ||
// Instead, React will render the new page components straight away, which will have selectors with the correct data. | ||
(0, react_1.useMemo)(function () { | ||
if (shouldHydrate.current) { | ||
hydrateOrchestrator(store, giapState, gspState, gsspState, gippState); | ||
shouldHydrate.current = false; | ||
} | ||
}, [store, giapState, gspState, gsspState, gippState]); | ||
}; | ||
// giapState stands for getInitialAppProps state | ||
var useWrappedStore = function (_a, displayName) { | ||
var _b, _c, _d, _e, _f, _g; | ||
if (displayName === void 0) { displayName = 'useWrappedStore'; } | ||
var giapState = _a.initialState, initialProps = _a.initialProps, props = __rest(_a, ["initialState", "initialProps"]); | ||
// getStaticProps state | ||
var gspState = (props === null || props === void 0 ? void 0 : props.__N_SSG) ? (_b = props === null || props === void 0 ? void 0 : props.pageProps) === null || _b === void 0 ? void 0 : _b.initialState : null; | ||
// getServerSideProps state | ||
var gsspState = (props === null || props === void 0 ? void 0 : props.__N_SSP) ? (_c = props === null || props === void 0 ? void 0 : props.pageProps) === null || _c === void 0 ? void 0 : _c.initialState : null; | ||
// getInitialPageProps state | ||
var gippState = !gspState && !gsspState ? (_e = (_d = props === null || props === void 0 ? void 0 : props.pageProps) === null || _d === void 0 ? void 0 : _d.initialState) !== null && _e !== void 0 ? _e : null : null; | ||
hydrate(); | ||
if (config.debug) { | ||
console.log('4.', displayName, 'created new store with', { | ||
giapState: giapState, | ||
gspState: gspState, | ||
gsspState: gsspState, | ||
gippState: gippState, | ||
}); | ||
console.log('4. useHydration done'); | ||
} | ||
var store = (0, react_1.useMemo)(function () { return initStore({ makeStore: makeStore }); }, []); | ||
useHybridHydrate(store, giapState, gspState, gsspState, gippState); | ||
var resultProps = props; | ||
// order is important! Next.js overwrites props from pages/_app with getStaticProps from page | ||
// @see https://github.com/zeit/next.js/issues/11648 | ||
if (initialProps && initialProps.pageProps) { | ||
resultProps.pageProps = __assign(__assign({}, initialProps.pageProps), props.pageProps); | ||
} | ||
// just some cleanup to prevent passing it as props, we need to clone props to safely delete initialState | ||
if ((_f = props === null || props === void 0 ? void 0 : props.pageProps) === null || _f === void 0 ? void 0 : _f.initialState) { | ||
resultProps = __assign(__assign({}, props), { pageProps: __assign({}, props.pageProps) }); | ||
delete resultProps.pageProps.initialState; | ||
} | ||
// unwrap getInitialPageProps | ||
if ((_g = resultProps === null || resultProps === void 0 ? void 0 : resultProps.pageProps) === null || _g === void 0 ? void 0 : _g.initialProps) { | ||
resultProps.pageProps = __assign(__assign({}, resultProps.pageProps), resultProps.pageProps.initialProps); | ||
delete resultProps.pageProps.initialProps; | ||
} | ||
return { store: store, props: __assign(__assign({}, initialProps), resultProps) }; | ||
return { hydrating: false }; | ||
}; | ||
var withRedux = function (Component) { | ||
console.warn('/!\\ You are using legacy implementation. Please update your code: use createWrapper() and wrapper.useWrappedStore().'); | ||
//TODO Check if pages/_app was wrapped so there's no need to wrap a page itself | ||
var WrappedComponent = function (props) { | ||
var _a = useWrappedStore(props, WrappedComponent.displayName), store = _a.store, combinedProps = _a.props; | ||
return (react_1.default.createElement(react_redux_1.Provider, { store: store }, | ||
react_1.default.createElement(Component, __assign({}, combinedProps)))); | ||
}; | ||
WrappedComponent.displayName = "withRedux(".concat(Component.displayName || Component.name || 'Component', ")"); | ||
if ('getInitialProps' in Component) { | ||
WrappedComponent.getInitialProps = Component.getInitialProps; | ||
} | ||
return WrappedComponent; | ||
}; | ||
return { | ||
@@ -347,14 +405,7 @@ getServerSideProps: getServerSideProps, | ||
getInitialPageProps: getInitialPageProps, | ||
withRedux: withRedux, | ||
useWrappedStore: useWrappedStore, | ||
useStore: useStore, | ||
useHydration: useHydration, | ||
}; | ||
}; | ||
exports.createWrapper = createWrapper; | ||
// Legacy | ||
// eslint-disable-next-line import/no-anonymous-default-export | ||
exports.default = (function (makeStore, config) { | ||
if (config === void 0) { config = {}; } | ||
console.warn('/!\\ You are using legacy implementation. Please update your code: use createWrapper() and wrapper.withRedux().'); | ||
return (0, exports.createWrapper)(makeStore, config).withRedux; | ||
}); | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "next-redux-wrapper", | ||
"version": "8.1.0", | ||
"version": "9.0.0-rc.1", | ||
"description": "Redux wrapper for Next.js", | ||
@@ -13,3 +13,2 @@ "main": "lib/index.js", | ||
"test": "jest", | ||
"test:quick": "yarn test", | ||
"clean": "rimraf lib es6 types coverage", | ||
@@ -21,7 +20,10 @@ "build": "concurrently 'yarn:build:tsc:*'", | ||
"start:tsc:es5": "yarn build:tsc:es5 --watch --preserveWatchOutput", | ||
"start:tsc:es6": "yarn build:tsc:es6 --watch --preserveWatchOutput" | ||
"start:tsc:es6": "yarn build:tsc:es6 --watch --preserveWatchOutput", | ||
"wait": "wait-on -v es6/index.js es6/index.d.ts lib/index.js lib/index.d.ts", | ||
"publish:release": "yarn wait && npm version ${TAG} && npm publish" | ||
}, | ||
"devDependencies": { | ||
"@testing-library/react-hooks": "8.0.1", | ||
"@types/jest": "27.0.1", | ||
"@types/react": "17.0.37", | ||
"@types/react": "18.0.27", | ||
"@types/react-redux": "7.1.20", | ||
@@ -34,7 +36,7 @@ "@types/react-test-renderer": "17.0.1", | ||
"next": "12.0.4", | ||
"next-redux-wrapper-configs": "^8.1.0", | ||
"react": "17.0.2", | ||
"react-dom": "17.0.2", | ||
"next-redux-wrapper-configs": "*", | ||
"react": "18.2.0", | ||
"react-dom": "18.2.0", | ||
"react-redux": "7.2.6", | ||
"react-test-renderer": "17.0.2", | ||
"react-test-renderer": "18.2.0", | ||
"redux": "4.1.2", | ||
@@ -44,3 +46,4 @@ "redux-promise-middleware": "6.1.2", | ||
"ts-jest": "27.0.7", | ||
"typescript": "4.5.2" | ||
"typescript": "4.9.5", | ||
"wait-on": "7.0.1" | ||
}, | ||
@@ -61,4 +64,3 @@ "peerDependencies": { | ||
"homepage": "https://github.com/kirill-konshin/next-redux-wrapper", | ||
"license": "MIT", | ||
"gitHead": "8e098f77691704b69d745b19ef3f0e9ca73d0776" | ||
"license": "MIT" | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
50384
21
5
826
1
1