next-redux-wrapper
Advanced tools
Comparing version 1.3.5 to 2.0.0-beta.2
{ | ||
"name": "next-redux-wrapper", | ||
"version": "1.3.5", | ||
"version": "2.0.0-beta.2", | ||
"description": "Redux wrapper for Next.js", | ||
"main": "src/index.js", | ||
"main": "lib/index.js", | ||
"scripts": { | ||
"test": "jest", | ||
"start": "next", | ||
"build": "next build", | ||
"serve": "next start" | ||
"test": "NODE_ENV=test jest", | ||
"clean": "rimraf lib", | ||
"babel": "babel --ignore spec.js,test.js --out-dir lib src", | ||
"build": "npm run clean && npm run babel", | ||
"next": "next", | ||
"watch": "npm run babel -- --watch", | ||
"start": "npm-run-all -p watch next", | ||
"prepublishOnly": "npm run build && npm test" | ||
}, | ||
"author": "Kirill Konshin", | ||
"dependencies": { | ||
"@babel/runtime": "^7.0.0-beta.42" | ||
}, | ||
"devDependencies": { | ||
"babel-jest": "^20.0.3", | ||
"babel-preset-react-app": "^3.0.1", | ||
"jest": "^20.0.4", | ||
"next": "^2.4.3", | ||
"react": "^15.4.2", | ||
"react-dom": "^15.6.1", | ||
"react-redux": "^5.0.2", | ||
"react-test-renderer": "^15.4.2", | ||
"redux": "^3.6.0", | ||
"redux-promise-middleware": "^4.2.0" | ||
"@babel/cli": "7.0.0-beta.42", | ||
"@babel/core": "7.0.0-beta.42", | ||
"@babel/plugin-transform-runtime": "7.0.0-beta.42", | ||
"@types/jest": "22.2.3", | ||
"@types/jest-environment-puppeteer": "2.2.0", | ||
"@types/puppeteer": "1.2.1", | ||
"babel-core": "7.0.0-bridge.0", | ||
"babel-jest": "22.4.3", | ||
"jest": "22.4.3", | ||
"jest-puppeteer": "2.4.0", | ||
"next": "canary", | ||
"npm-run-all": "4.1.2", | ||
"puppeteer": "1.3.0", | ||
"react": "16.3.2", | ||
"react-dom": "16.3.2", | ||
"react-redux": "5.0.7", | ||
"react-test-renderer": "16.2.0", | ||
"redux": "4.0.0", | ||
"redux-promise-middleware": "5.1.1", | ||
"rimraf": "2.6.2" | ||
}, | ||
"peerDependencies": { | ||
"react": "*", | ||
"react-redux": "*" | ||
"react-redux": "*", | ||
"next": ">=6.0.0 || 6.0.0-* || canary" | ||
}, | ||
@@ -40,16 +58,3 @@ "repository": { | ||
}, | ||
"license": "MIT", | ||
"jest": { | ||
"testRegex": "(/src/.*\\.spec.js)$", | ||
"collectCoverage": true, | ||
"testEnvironment": "jsdom" | ||
}, | ||
"babel": { | ||
"presets": [ | ||
"react-app" | ||
] | ||
}, | ||
"dependencies": { | ||
"object.assign": "^4.0.4" | ||
} | ||
"license": "MIT" | ||
} |
187
README.md
Redux wrapper for Next.js | ||
========================= | ||
![Build status](https://travis-ci.org/kirill-konshin/next-redux-wrapper.svg?branch=master) | ||
## Usage | ||
:warning: This will work only with NextJS 6.x :warning: | ||
If you're looking for a version for NextJS 5.x (the one for individual pages) use [1.x branch](https://github.com/kirill-konshin/next-redux-wrapper/tree/1.x). | ||
- [Usage](#usage) | ||
- [How it works](#how-it-works) | ||
- [Async actions in `getInitialProps`](#async-actions-in-getinitialprops) | ||
- [Usage with Immutable.JS](#usage-with-immutablejs) | ||
- [Usage with Redux Persist](#usage-with-redux-persist) | ||
- [Resources](#resources) | ||
## Installation | ||
```bash | ||
npm install next-redux-wrapper --save | ||
npm install next-redux-wrapper@next --save | ||
``` | ||
Wrapper has to be attached your page components (located in `/pages`). For safety it is recommended | ||
to wrap all pages, no matter if they use Redux or not, so that you should not care about it anymore in | ||
all child components. | ||
Wrapper has to be attached your `_app` component (located in `/pages`). All other pages may use regular `connect` | ||
function of `react-redux`. | ||
@@ -18,5 +29,6 @@ Here is the minimal setup (`makeStore` and `reducer` usually are located in other files): | ||
```js | ||
import React, {Component} from "react"; | ||
// pages/_app.js | ||
import React from "react"; | ||
import {createStore} from "redux"; | ||
import withRedux from "next-redux-wrapper"; | ||
import {withReduxApp} from "next-redux-wrapper"; | ||
@@ -43,2 +55,33 @@ const reducer = (state = {foo: ''}, action) => { | ||
class MyApp extends React.Component { | ||
static async getInitialProps({Component, ctx}) { | ||
// we can dispatch from here too | ||
ctx.store.dispatch({type: 'FOO', payload: 'foo'}); | ||
const pageProps = Component.getInitialProps ? await Component.getInitialProps(ctx) : {}; | ||
return {pageProps}; | ||
} | ||
render() { | ||
const {Component, pageProps} = this.props; | ||
return ( | ||
<Component {...pageProps} /> | ||
); | ||
} | ||
} | ||
export default withReduxApp(makeStore)(MyApp); | ||
``` | ||
And then actual page components can be simply connected: | ||
```js | ||
import React, {Component} from "react"; | ||
import {connect} from "react-redux"; | ||
class Page extends Component { | ||
@@ -70,28 +113,15 @@ static getInitialProps({store, isServer, pathname, query}) { | ||
The `withRedux` function accepts `makeStore` as first argument, all other arguments are internally passed to React | ||
Redux's `connect()` function for simplicity. The `makeStore` function will receive initial state as one argument and | ||
should return a new instance of redux store each time when called, no memoization needed here, it is automatically done | ||
The `withRedux` function accepts `makeStore` as first argument. The `makeStore` function will receive initial state as one argument and | ||
should return a new instance of Redux `store` each time when called, no memoization needed here, it is automatically done | ||
inside the wrapper. | ||
`withRedux` also optionally accepts an object. In this case only 1 parameter is passed which can contain the following | ||
configuration properties: | ||
When `makeStore` is invoked it is also provided with a configuration object as the second parameter, which includes: | ||
- `createStore` (required, function) : the `makeStore` function as described above | ||
- `storeKey` (optional, string) : the key used on `window` to persist the store on the client | ||
- `debug` (optional, boolean) : enable debug logging | ||
- `mapStateToProps`, `mapDispatchToProps`, `mergeProps` (optional, functions) : functions to pass to `react-redux` `connect` method | ||
- `connectOptions` (optional, object) : configuration to pass to `react-redux` `connect` method | ||
When `makeStore` is invoked it is also provided a configuration object as the second parameter, which includes: | ||
- `isServer` (boolean): `true` if called while on the server rather than the client | ||
- `req` (Request): The `next.js` `getInitialProps` context `req` parameter | ||
- `res` (Request): The `next.js` `getInitialProps` context `req` parameter | ||
- `query` (object): The `next.js` `getInitialProps` context `query` parameter | ||
The object also includes all configuration as passed to `withRedux` if called with an object of configuration properties. | ||
The object also includes all configuration as passed to `withRedux`. | ||
**Use `withRedux` to wrap only top level pages! All other components should keep using regular `connect` function of | ||
React Redux.** | ||
Although it is possible to create server or client specific logic in both `createStore` function and `getInitialProps` | ||
@@ -101,2 +131,5 @@ method I highly don't recommend to have different behavior. This may cause errors and checksum mismatches which in turn | ||
Keep in mind that whatever you do in `_app` is also affecting the NextJS error page, so if you `dispatch`, | ||
set something on `req` and checki it to prevent double `dispatch`. | ||
I don't recommend to use `withRedux` in both top level pages and `_document.js` files, Next.JS | ||
@@ -150,2 +183,106 @@ [does not have provide](https://github.com/zeit/next.js/issues/1267) a reliable way to determine the sequence when | ||
## Usage with Redux Persist | ||
Honestly, I think that putting a persistence gate is not necessary because server can already send *some* HTML with *some* state, so it's better to show it right away and then wait for `REHYDRATE` action to happen to show additional delta coming from persistence storage. That's why we use Server Side Rendering in a first place. | ||
But, for those who actually want to block the UI while rehydration is happening, here is the solution (still hacky though). | ||
```js | ||
// lib/redux.js | ||
import logger from 'redux-logger'; | ||
import {applyMiddleware, createStore} from 'redux'; | ||
const SET_CLIENT_STATE = 'SET_CLIENT_STATE'; | ||
export const reducer = (state, {type, payload}) => { | ||
if (type === SET_CLIENT_STATE) { | ||
return { | ||
...state, | ||
fromClient: payload | ||
}; | ||
} | ||
return state; | ||
}; | ||
const makeConfiguredStore = (reducer, initialState) => | ||
createStore(reducer, initialState, applyMiddleware(logger)); | ||
export const makeStore = (initialState, {isServer, req, debug, storeKey}) => { | ||
if (isServer) { | ||
initialState = initialState || {fromServer: 'foo'}; | ||
return makeConfiguredStore(reducer, initialState); | ||
} else { | ||
// we need it only on client side | ||
const {persistStore, persistReducer} = require('redux-persist'); | ||
const storage = require('redux-persist/lib/storage').default; | ||
const persistConfig = { | ||
key: 'nextjs', | ||
whitelist: ['fromClient'], // make sure it does not clash with server keys | ||
storage | ||
}; | ||
const persistedReducer = persistReducer(persistConfig, reducer); | ||
const store = makeConfiguredStore(persistedReducer, initialState); | ||
store.__persistor = persistStore(store); // Nasty hack | ||
return store; | ||
} | ||
}; | ||
export const setClientState = (clientState) => ({ | ||
type: SET_CLIENT_STATE, | ||
payload: clientState | ||
}); | ||
``` | ||
And then in NextJS `_app` page: | ||
```js | ||
// pages/_app.js | ||
import React from "react"; | ||
import withRedux from "next-redux-wrapper"; | ||
import {makeStore} from "./lib/redux"; | ||
import {PersistGate} from 'redux-persist/integration/react'; | ||
export default withRedux(makeStore, {debug: true})(class MyApp extends React.Component { | ||
render() { | ||
const {Component, pageProps, store} = this.props; | ||
return ( | ||
<PersistGate persistor={store.__persistor} loading: {<div>Loading</div>}> | ||
<Component {...pageProps} /> | ||
</PersistGate> | ||
); | ||
} | ||
}); | ||
``` | ||
And then in NextJS page: | ||
```js | ||
// pages/index.js | ||
import React from "react"; | ||
import {connect} from "react-redux"; | ||
export default connect( | ||
(state) => state, | ||
{setClientState} | ||
)(({fromServer, fromClient, setClientState}) => ( | ||
<div> | ||
<div>fromServer: {fromServer}</div> | ||
<div>fromClient: {fromClient}</div> | ||
<div><button onClick={e => setClientState('bar')}>Set Client State</button></div> | ||
</div> | ||
)); | ||
``` | ||
## Resources | ||
@@ -152,0 +289,0 @@ |
200
src/index.js
@@ -1,36 +0,36 @@ | ||
var React = require('react'); | ||
var ReactRedux = require('react-redux'); | ||
var assign = require('object.assign'); | ||
import React from "react"; | ||
import {Provider} from "react-redux"; | ||
import BaseApp, {Container} from "next/app"; | ||
var connect = ReactRedux.connect; | ||
var Provider = ReactRedux.Provider; | ||
let _Promise = Promise; | ||
let _debug = false; | ||
const DEFAULT_KEY = '__NEXT_REDUX_STORE__'; | ||
var _Promise; | ||
var _debug = false; | ||
var skipMerge = ['initialState', 'initialProps', 'isServer', 'store']; | ||
var DEFAULT_KEY = '__NEXT_REDUX_STORE__'; | ||
var isBrowser = typeof window !== 'undefined'; | ||
export const setPromise = Promise => _Promise = Promise; | ||
function initStore(makeStore, initialState, context, config) { | ||
var req = context.req; | ||
var isServer = !!req && !isBrowser; | ||
var storeKey = config.storeKey; | ||
/** | ||
* @param makeStore | ||
* @param initialState | ||
* @param ctx | ||
* @param config | ||
* @return {{getState: function, dispatch: function}} | ||
*/ | ||
const initStore = (makeStore, initialState, ctx, config) => { | ||
var options = assign({}, config, { | ||
isServer: isServer, | ||
req: req, | ||
res: context.res, | ||
query: context.query | ||
}); | ||
const {req} = ctx; | ||
const isServer = !!req; | ||
const {storeKey} = config; | ||
const options = { | ||
...ctx, | ||
...config, | ||
isServer | ||
}; | ||
// Always make a new store if server | ||
if (isServer) { | ||
if (!req._store) { | ||
req._store = makeStore(initialState, options); | ||
} | ||
if (!req._store) req._store = makeStore(initialState, options); // page and error should share one store | ||
return req._store; | ||
} | ||
if (!isBrowser) return null; | ||
// Memoize store if client | ||
@@ -43,74 +43,69 @@ if (!window[storeKey]) { | ||
} | ||
}; | ||
module.exports = function(createStore) { | ||
/** | ||
* @param createStore | ||
* @param config | ||
* @return {function(App): {getInitialProps, new(): WrappedApp, prototype: WrappedApp}} | ||
*/ | ||
export default (createStore, config = {}) => { | ||
var config = {storeKey: DEFAULT_KEY, debug: _debug}; | ||
var connectArgs; | ||
config = {storeKey: DEFAULT_KEY, debug: _debug, ...config}; | ||
// Ensure backwards compatibility, the config object should come last after connect arguments. | ||
if (typeof createStore === 'object') { | ||
return (App) => (class WrappedApp extends BaseApp { | ||
var wrappedConfig = createStore; | ||
static displayName = `withRedux(${App.displayName || App.name || 'App'})`; | ||
if (!({}).hasOwnProperty.call(wrappedConfig, 'createStore')) { | ||
throw new Error('Missing createStore function'); | ||
} | ||
createStore = wrappedConfig.createStore; | ||
static getInitialProps = async (appCtx = {}) => { | ||
// Set all config keys if they exist. | ||
if (({}).hasOwnProperty.call(wrappedConfig, 'debug')) { | ||
config.debug = wrappedConfig.debug; | ||
} | ||
const {ctx: {req} = {}, ctx = {}} = appCtx; | ||
if (({}).hasOwnProperty.call(wrappedConfig, 'storeKey')) { | ||
config.storeKey = wrappedConfig.storeKey; | ||
} | ||
const isServer = !!req; | ||
// Map the connect arguments from the passed in config object. | ||
connectArgs = [ | ||
wrappedConfig.mapStateToProps || undefined, | ||
wrappedConfig.mapDispatchToProps || undefined, | ||
wrappedConfig.mergeProps || undefined, | ||
wrappedConfig.connectOptions || undefined, | ||
]; | ||
const store = initStore( | ||
createStore, | ||
undefined /** initialState **/, | ||
ctx, | ||
config | ||
); | ||
} else { | ||
connectArgs = [].slice.call(arguments).slice(1); | ||
} | ||
if (config.debug) console.log('1. WrappedApp.getInitialProps wrapper got the store with state', store.getState()); | ||
return function(Cmp) { | ||
const initialProps = (App.getInitialProps ? await App.getInitialProps.call(App, { | ||
...appCtx, | ||
ctx: { | ||
...ctx, | ||
store, | ||
isServer | ||
} | ||
}) : {}); | ||
// Since provide should always be after connect we connect here | ||
var ConnectedCmp = (connect.apply(null, connectArgs))(Cmp); | ||
if (config.debug) console.log('3. WrappedApp.getInitialProps has store state', store.getState()); | ||
function WrappedCmp(props) { | ||
return { | ||
isServer, | ||
store, | ||
initialState: store.getState(), | ||
initialProps: initialProps | ||
}; | ||
}; | ||
props = props || {}; | ||
render() { | ||
var initialState = props.initialState || {}; | ||
var initialProps = props.initialProps || {}; | ||
var hasStore = props.store && props.store.dispatch && props.store.getState; | ||
var store = hasStore | ||
? props.store | ||
: initStore(createStore, initialState, {}, config); // client case, no store but has initialState | ||
let {initialState, initialProps, store, isServer, ...props} = this.props; | ||
if (!store) { | ||
console.error('Attention, withRedux has to be used only for top level pages, all other components must be wrapped with React Redux connect!'); | ||
console.error('Check ' + Cmp.name + ' component.'); | ||
console.error('Automatic fallback to connect has been performed, please check your code.'); | ||
return React.createElement(ConnectedCmp, props); | ||
} | ||
const hasStore = store && ('dispatch' in store) && ('getState' in store); | ||
if (config.debug) console.log(Cmp.name, '- 4. WrappedCmp.render', (hasStore ? 'picked up existing one,' : 'created new store with'), 'initialState', initialState); | ||
store = hasStore ? store : initStore(createStore, initialState, {}, config); // client case, no store but has initialState | ||
// Fix for _document | ||
var mergedProps = {}; | ||
Object.keys(props).forEach(function(p) { if (!~skipMerge.indexOf(p)) mergedProps[p] = props[p]; }); | ||
Object.keys(initialProps || {}).forEach(function(p) { mergedProps[p] = initialProps[p]; }); | ||
if (config.debug) console.log('4. WrappedApp.render', (hasStore ? 'picked up existing one,' : 'created new store with'), 'initialState', initialState); | ||
return React.createElement( //FIXME This will create double Provider for _document case | ||
Provider, | ||
{store: store}, | ||
React.createElement(ConnectedCmp, mergedProps) | ||
// <Container> must be on top of hierarchy for HMR to work | ||
// Cmp render must return something like <Component/> | ||
return ( | ||
<Container> | ||
<Provider store={store}> | ||
<App {...initialProps} {...props} store={store} isServer={isServer}/> | ||
</Provider> | ||
</Container> | ||
); | ||
@@ -120,47 +115,4 @@ | ||
WrappedCmp.getInitialProps = function(ctx) { | ||
}); | ||
return new _Promise(function(res) { | ||
ctx = ctx || {}; | ||
if (config.debug) console.log(Cmp.name, '- 1. WrappedCmp.getInitialProps wrapper', (ctx.req && ctx.req._store ? 'takes the req store' : 'creates the store')); | ||
ctx.isServer = !!ctx.req; | ||
ctx.store = initStore(createStore, undefined /** initialState **/, {req: ctx.req, query: ctx.query, res: ctx.res}, config); | ||
res(_Promise.all([ | ||
ctx.isServer, | ||
ctx.store, | ||
ctx.req, | ||
Cmp.getInitialProps ? Cmp.getInitialProps.call(Cmp, ctx) : {} | ||
])); | ||
}).then(function(arr) { | ||
if (config.debug) console.log(Cmp.name, '- 3. WrappedCmp.getInitialProps has store state', arr[1].getState()); | ||
return { | ||
isServer: arr[0], | ||
store: arr[1], | ||
initialState: arr[1].getState(), | ||
initialProps: arr[3] | ||
}; | ||
}); | ||
}; | ||
return WrappedCmp; | ||
}; | ||
}; | ||
module.exports.setPromise = function(Promise) { | ||
_Promise = Promise; | ||
}; | ||
module.exports.setDebug = function(debug) { | ||
_debug = debug; | ||
}; | ||
module.exports.setPromise(Promise); | ||
}; |
@@ -5,4 +5,6 @@ import React from "react"; | ||
import renderer from "react-test-renderer"; | ||
import wrapper from "./index"; | ||
import withRedux from "./index"; | ||
global.window = {}; //FIXME Move to test env https://github.com/smooth-code/jest-puppeteer#extend-puppeteerenvironment | ||
const reducer = (state = {reduxStatus: 'init'}, action) => { | ||
@@ -18,10 +20,8 @@ switch (action.type) { | ||
const makeStore = (initialState) => { | ||
return createStore(reducer, initialState, applyMiddleware(promiseMiddleware())); | ||
}; | ||
const makeStore = (initialState) => createStore(reducer, initialState, applyMiddleware(promiseMiddleware())); | ||
class SyncPage extends React.Component { | ||
static getInitialProps({store}) { | ||
store.dispatch({type: 'FOO', payload: 'foo'}); | ||
static getInitialProps({ctx}) { | ||
ctx.store.dispatch({type: 'FOO', payload: 'foo'}); | ||
return {custom: 'custom'}; | ||
@@ -31,6 +31,7 @@ } | ||
render() { | ||
const {store, ...props} = this.props; | ||
return ( | ||
<div> | ||
<div className="redux">{this.props.reduxStatus}</div> | ||
<div className="custom">{this.props.custom}</div> | ||
{JSON.stringify(props)} | ||
{JSON.stringify(store.getState())} | ||
</div> | ||
@@ -42,16 +43,11 @@ ) | ||
function someAsyncAction() { | ||
return { | ||
type: 'FOO', | ||
payload: new Promise((res) => { res('foo'); }) | ||
} | ||
} | ||
const someAsyncAction = ({ | ||
type: 'FOO', | ||
payload: new Promise(res => res('foo')) | ||
}); | ||
class AsyncPage extends SyncPage { | ||
static getInitialProps({store}) { | ||
const action = someAsyncAction(); | ||
store.dispatch(action); | ||
return action.payload.then((payload) => { | ||
return {custom: 'custom'}; | ||
}); | ||
static async getInitialProps({ctx}) { | ||
await ctx.store.dispatch(someAsyncAction); | ||
return {custom: 'custom'}; | ||
} | ||
@@ -76,98 +72,10 @@ } | ||
test('simple store integration', async() => { | ||
const WrappedPage = wrapper(makeStore, state => state)(SyncPage); | ||
test('simple store integration', async () => { | ||
const WrappedPage = withRedux(makeStore)(SyncPage); | ||
await verifyComponent(WrappedPage); | ||
}); | ||
test('async store integration', async() => { | ||
const WrappedPage = wrapper(makeStore, state => state)(AsyncPage); | ||
test('async store integration', async () => { | ||
const WrappedPage = withRedux(makeStore)(AsyncPage); | ||
await verifyComponent(WrappedPage); | ||
}); | ||
const spyLog = jest.spyOn(global.console, 'log'); | ||
describe('createStore', () => { | ||
beforeEach(() => { | ||
spyLog.mockReset(); | ||
}); | ||
afterEach(() => { | ||
delete window.__NEXT_REDUX_STORE__; | ||
}); | ||
test('simple props', () => { | ||
const App = ({foo}) => (<div>{foo}</div>); | ||
const WrappedApp = wrapper(makeStore, state => state)(App); | ||
const component = renderer.create(<WrappedApp foo="foo"/>); | ||
expect(component.toJSON()).toMatchSnapshot(); | ||
}); | ||
test('advanced props', () => { | ||
const App = ({foo}) => (<div>{foo}</div>); | ||
const WrappedApp = wrapper({ createStore: makeStore, mapStateToProps: (state) => state })(App); | ||
const component = renderer.create(<WrappedApp foo="foo"/>); | ||
expect(component.toJSON()).toMatchSnapshot(); | ||
}); | ||
test('debug mode from options', async () => { | ||
const App = ({foo}) => (<div>{foo}</div>); | ||
const WrappedApp = wrapper({ | ||
createStore: (initialState, options) => { | ||
expect(options.debug).toBe(true); | ||
return createStore(reducer, initialState, applyMiddleware(promiseMiddleware())); | ||
}, | ||
debug: true | ||
})(App); | ||
const component = renderer.create(<WrappedApp/>); | ||
await WrappedApp.getInitialProps(); | ||
expect(spyLog).toHaveBeenCalledTimes(3); | ||
expect(component.toJSON()).toMatchSnapshot(); | ||
}); | ||
test('debug mode with setDebug method', async () => { | ||
const App = ({foo}) => (<div>{foo}</div>); | ||
wrapper.setDebug(true); | ||
const WrappedApp = wrapper((initialState, options) => { | ||
expect(options.debug).toBe(true); | ||
return createStore(reducer, initialState, applyMiddleware(promiseMiddleware())); | ||
})(App); | ||
const component = renderer.create(<WrappedApp/>); | ||
await WrappedApp.getInitialProps(); | ||
expect(spyLog).toHaveBeenCalledTimes(3); | ||
expect(component.toJSON()).toMatchSnapshot(); | ||
}); | ||
test('should throw if no createStore method', async () => { | ||
const App = ({foo}) => (<div>{foo}</div>); | ||
expect(() => wrapper({ | ||
debug: true | ||
})(App)).toThrow(); | ||
}); | ||
test('should be able to configure store key on window', async () => { | ||
const App = ({foo}) => (<div>{foo}</div>); | ||
const WrappedApp = wrapper({ | ||
createStore: makeStore, | ||
storeKey: 'TESTKEY' | ||
})(App); | ||
const component = renderer.create(<WrappedApp/>); | ||
await WrappedApp.getInitialProps(); | ||
expect(window.__NEXT_REDUX_STORE__).not.toBeDefined(); | ||
expect(window.TESTKEY).toBeDefined(); | ||
expect(component.toJSON()).toMatchSnapshot(); | ||
delete window.TESTKEY; | ||
}); | ||
test('should memoize store on client in window', async() => { | ||
const App = ({foo}) => (<div>{foo}</div>); | ||
const App1 = wrapper(makeStore, state => state)(App); | ||
renderer.create(<App1/>); | ||
expect(window.__NEXT_REDUX_STORE__).toBeDefined(); | ||
const App2 = wrapper((initialState, options) => { | ||
throw new Error('New store should not be created!'); | ||
}, state => state)(App); | ||
renderer.create(<App2 foo="foo"/>); | ||
expect(window.__NEXT_REDUX_STORE__).toBeDefined(); | ||
}); | ||
}); | ||
}); |
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
31544
495
289
4
20
1
1
+ Added@babel/runtime@7.26.0(transitive)
+ Addedregenerator-runtime@0.14.1(transitive)
- Removedobject.assign@^4.0.4
- Removedcall-bind@1.0.7(transitive)
- Removeddefine-data-property@1.1.4(transitive)
- Removeddefine-properties@1.2.1(transitive)
- Removedes-define-property@1.0.0(transitive)
- Removedes-errors@1.3.0(transitive)
- Removedfunction-bind@1.1.2(transitive)
- Removedget-intrinsic@1.2.4(transitive)
- Removedgopd@1.0.1(transitive)
- Removedhas-property-descriptors@1.0.2(transitive)
- Removedhas-proto@1.0.3(transitive)
- Removedhas-symbols@1.0.3(transitive)
- Removedhasown@2.0.2(transitive)
- Removedobject-keys@1.1.1(transitive)
- Removedobject.assign@4.1.5(transitive)
- Removedset-function-length@1.2.2(transitive)