Cannot find store in redux connect in a Jest Test - redux

I am getting the following error while trying to setup a component in my jest test :
Invariant Violation: Could not find "store" in either the context or
props of "Connect(TestComponent)". Either wrap the root component in a
, or explicitly pass "store" as a prop to
"Connect(TestComponent)".
My test looks like this :
import React from 'react';
import { shallow } from 'enzyme';
import { Map } from 'immutable';
import { createStore } from 'redux';
import TestComponent from '../TestComponent ';
import { Provider } from 'react-redux';
describe('test ', () => {
test(' testing', () => {
const state = { blah: 1 };
const reducer = s => s; //dummy reducer
const store = createStore(reducer, state);
const component = (
<Provider store={store}>
<TestComponent />
</Provider>
);
const wrapper = shallow(component);
let json = wrapper.html();
expect(json).toMatchSnapshot();
});
});
and the component being tested looks like this :
import React, { Component } from 'react';
import { connect } from 'react-redux';
class TestComponent extends Component {
render = () => {};
}
function mapStateToProps(state) {
return { blah: state };
}
export default connect(
mapStateToProps,
null
)(TestComponent);
I'm not exactly sure what is wrong with this. It all looks kosher to me. When calling html() it cannot find the store.

Try making your wrapper component a function instead of variable:
export const testWrapper = Component => {
return (
<Provider store={store}>
{Component}
</Provider>
);
};
const wrapper = shallow(testWrapper(<TestComponent/>));
let json = wrapper.html();
expect(json).toMatchSnapshot();
Also I'd recommend looking into redux-mock-store for testing.

Here is the solution, you need to create mocked store and pass it to the component wrapped by connect function.
index.ts:
import React, { Component } from 'react';
import { connect } from 'react-redux';
interface ITestComponentProps {
blah: any;
store: any;
}
export class TestComponent extends Component<ITestComponentProps> {
constructor(props) {
super(props);
}
public render() {
return <div>{this.props.blah}</div>;
}
}
function mapStateToProps(state) {
return { blah: state };
}
export default connect(mapStateToProps)(TestComponent);
Unit test:
import React from 'react';
import { shallow } from 'enzyme';
import ConnectedTestComponent, { TestComponent } from './';
import configureMockStore from 'redux-mock-store';
const state = 1;
const mockStore = configureMockStore();
const store = mockStore(state);
describe('test', () => {
it('t1', () => {
const connetedTestComponentWrapper = shallow(<ConnectedTestComponent store={store} />);
const testComponentWrapper = connetedTestComponentWrapper.find(TestComponent);
expect(testComponentWrapper.prop('blah')).toBe(state);
expect(testComponentWrapper.html()).toMatchSnapshot();
});
});
Unit test result with 100% coverage:
PASS src/stackoverflow/57290601/index.spec.tsx
test
✓ t1 (54ms)
-----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
-----------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
index.tsx | 100 | 100 | 100 | 100 | |
-----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 1 passed, 1 total
Time: 3.089s, estimated 5s
snapshot:
// Jest Snapshot v1
exports[`test t1 1`] = `"<div>1</div>"`;
Here is the completed demo: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/57290601

Related

Error: Cannot read properties of undefined (reading 'getState')

I am getting the error "Error: Cannot read properties of undefined (reading 'getState')"
Not sure what is the issue but I have followed all instructions. I do not resolve it at all after searching everything. My app is in next.js and I want to add redux into it. Here is my code
/* pages/_app.js */
import App from "next/app"
import { Provider } from "react-redux"
import React from "react"
import { createWrapper } from "next-redux-wrapper"
import store from "../store/store"
class MyApp extends App {
static async getInitialProps({ Component, ctx }) {
const pageProps = Component.getInitialProps
? await Component.getInitialProps(ctx)
: {}
//Anything returned here can be accessed by the client
return { pageProps: pageProps }
}
render() {
//pageProps that were returned from 'getInitialProps' are stored in the props i.e. pageprops
const { Component, pageProps, store } = this.props
return (
<Provider store={store}>
<Component {...pageProps} />
</Provider>
)
}
}
//makeStore function that returns a new store for every request
const makeStore = () => store
const wrapper = createWrapper(makeStore)
export default wrapper.withRedux(MyApp)
//store/store.js
import { createStore, applyMiddleware } from "redux"
import thunk from "redux-thunk"
import rootReducer from "../reducers/rootReducer"
import { composeWithDevTools } from "redux-devtools-extension"
const initialState = { counter: 0 }
const middleware = [thunk]
const store = createStore(
rootReducer,
initialState,
composeWithDevTools(
applyMiddleware(...middleware)
// other store enhancers if any
)
)
export default store
///reducers/rootReducer.js
import counterReducer from "./counterReducer"
import { combineReducers } from "redux"
const rootReducer = combineReducers({
counter: counterReducer,
})
export default rootReducer
//counterReducer.js //
const counterReducer = (state = { value: 0 }, action) => {
return { ...state }
}
export default counterReducer

I have successfully implemented the redux-persist with next-redux-wrapper in next js

Im getting data from the external api and storing it in the reducer.And im using redux-persist to persist the state while navigating from one page to another.But i have made left the whiteList as an empty array but all the state are being persisted?Need help
import "../assets/css/style.scss";
import "owl.carousel/dist/assets/owl.carousel.css";
import "owl.carousel/dist/assets/owl.theme.default.css";
import Layout from "../component/Layout/Layout";
import { wrapper } from "../redux/store";
import { useEffect } from "react";
import { useStore } from "react-redux";
function MyApp({ Component, pageProps }) {
const store = useStore((store) => store);
useEffect(() => {
{
typeof document !== undefined
? require("bootstrap/dist/js/bootstrap.bundle")
: null;
}
}, []);
return (
<Layout>
<Component {...pageProps} />;
</Layout>
);
}
export default wrapper.withRedux(MyApp);
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import { composeWithDevTools } from "redux-devtools-extension";
import { persistStore, persistReducer } from "redux-persist";
import storage from "redux-persist/lib/storage";
import rootReducer from "./index";
import { createWrapper, HYDRATE } from "next-redux-wrapper";
const middleware = [thunk];
let initialState={}
// BINDING MIDDLEWARE
const bindMiddleware = (middleware) => {
if (process.env.NODE_ENV !== "production") {
return composeWithDevTools(applyMiddleware(...middleware));
}
return applyMiddleware(...middleware);
};
const makeStore = ({ isServer }) => {
if (isServer) {
//If it's on server side, create a store
return createStore(rootReducer,initialState, bindMiddleware(middleware));
} else {
//If it's on client side, create a store which will persis
const persistConfig = {
key: "root",
storage: storage,
whiteList: [],
};
const persistedReducer = persistReducer(persistConfig, rootReducer);
const store = createStore(persistedReducer,initialState, bindMiddleware(middleware));
store.__persisitor = persistStore(store); // This creates a persistor object & push that
persisted object to .__persistor, so that we can avail the persistability feature
return store;
}
};
// export an assembled wrapper
export const wrapper = createWrapper(makeStore);
If you keep the whitelist an empty array then nothing will be persisted.
You have to put inside that string array the redux reducers values you want to be persisted.
Example:
const persistConfig = {
key: 'root',
storage: storage,
whiteList: ['cart', 'form', 'user'],
};

Why this error is coming TypeError: Cannot read property 'type' of undefined

How to solve this error TypeError: Cannot read property 'type' of undefined.
I am very new to react-redux and redux, simply I am trying to do state management through redux.
For this I installed react-redux and redux npm packages. And I created a store, In store a have reducer.js file. Even I imported Provider and store in index.js. Help to resolve this issue.
In store folder I have reducer.js file
This is reducer.js file code
const initialState = {
age: 21
}
const reducer = (state = initialState, action) => {
const newState = {state};
if(action.type === 'AGE_UP') {
newState.age++
}
if(action.type === 'AGE_DOWN') {
newState.age--
}
return newState;
};
export default reducer
This is App.js
import React, { Component } from 'react';
import './App.css'
import { connect } from 'react-redux'
class App extends Component {
render() {
return (
<div className='App'>
<div>Age: <span>{this.props.age}</span></div>
<button onClick={this.props.onAgeUp}>Age UP</button>
<button onClick={this.props.onAgeDown}>Age Down</button>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
age:state.age
}
}
const mapDispatchToProps = (dispatch) => {
return {
onAgeUp: () => dispatch({type: 'AGE_UP'}),
onAgeDown: () => dispatch({type: 'AGE_DOWN'})
}
}
// export default connect()(App)
export default connect(mapStateToProps,mapDispatchToProps) (App)
This is index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reducer from './store/reducer';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import * as serviceWorker from './serviceWorker';
const store = createStore({}, reducer);
ReactDOM.render(<Provider projectStore={store}><App></App></Provider>, document.getElementById('root'));
serviceWorker.unregister();
I can see multiple issues in your code:
createStore takes reducer as the first argument, look at API documentation, try this:
const store = createStore(reducer);
Provider has prop store and not projectStore, look at API documentation, try this:
ReactDOM.render(<Provider store={store}><App></App></Provider>, document.getElementById('root'));
You probably mutate your state in a reducer, try this:
const reducer = (state = initialState, action) => {
// it is better to use switch instead of if in reducer, for sake of readability
switch (action.type) {
case 'AGE_UP':
// it is better to have return statement here, it is more robust to developer errors
// it is better to use object spread and return result here than creating `newState` variable, you will do less errors
return {...state, age: state.age + 1}
case 'AGE_DOWN':
return {...state, age: state.age - 1}
}
return state;
};

React, Redux app works on localhost but not working on production

When i do a production build i am getting store not found error. Interestingly Redux store is not being initialised.
The error message:
{ Invariant Violation: Could not find "store" in the context of "Connect(Form(LoginForm))". Either wrap the root
component in a , or pass a custom React context provider to and the corresponding React context consumer to Connect(Form(LoginForm)) in connect options.
import Immutable from 'immutable'
import thunkMiddleware from 'redux-thunk'
import { createLogger } from 'redux-logger'
import { createStore, applyMiddleware, compose } from 'redux'
import config from 'config'
import rootReducer from './reducers/index.js'
function createMiddlewares ({ isServer }) {
let middlewares = [
thunkMiddleware
]
if (config.env === 'development' && typeof window !== 'undefined') {
middlewares.push(createLogger({
level: 'info',
collapsed: true,
stateTransformer: (state) => {
let newState = {}
for (let i of Object.keys(state)) {
if (Immutable.Iterable.isIterable(state[i])) {
newState[i] = state[i].toJS()
} else {
newState[i] = state[i]
}
}
return newState
}
}))
}
return middlewares
}
function immutableChildren (obj) {
let state = {}
Object.keys(obj).forEach((key) => {
state[key] = Immutable.fromJS(obj[key])
})
return state
}
export default (initialState = {}, context) => {
let { isServer } = context
let middlewares = createMiddlewares({ isServer })
let state = immutableChildren(initialState)
return createStore(
rootReducer,
state,
compose(applyMiddleware(...middlewares))
)
}
import withRedux from 'next-redux-wrapper'
import { withRouter } from 'next/router'
import { Provider } from 'react-redux'
import App, { Container } from 'next/app'
import { checkForPopup } from "./helpers/popup.js";
import createStore from './redux/createStore.js'
class MyApp extends App {
static async getInitialProps ({ Component, ctx }) {
return {
pageProps: Component.getInitialProps
? await Component.getInitialProps(ctx)
: {}
}
}
render() {
const { Component, pageProps, store, router } = this.props
return (
<Container>
<Provider store={store}>
<Component router={router} {...pageProps} />
</Provider>
</Container>
);
}
componentDidMount() {
checkForPopup();
}
}
export default withRedux(createStore)(
withRouter(MyApp)
)
Based on #wamba-kevin's comment, when running express in production, you prefix the start script with NODE_ENV=production. I did the same with next and it worked.

composeWithTracker returns undefined arguments

This Meteor client code prints into is undefined to the console. It is expected to print the value of the info.description property passed to the composeWithTracker(composer)(Info) at the bottom of the code.
Where did I go wrong? Thanks
//--------------- carInfo.jsx ---------------
import React from 'react';
const renderWhenData = ( info ) => {
console.log( 'info is ' +info); //<<<<<<<<<<<<<<<<<<< undefined
if ( info ) {
return <span>{ info.description }</span>;
};
export let Info = ( { info } ) => (
<p>{ renderWhenData( info ) }</p>
);
//--------------- carClass.jsx ---------------
import React from 'react';
import ReactDOM from 'react-dom';
import { composeWithTracker } from 'react-komposer';
import { Info } from '../ui/carInfo.jsx';
const composer = (props, onData) => {
const subscription = Meteor.subscribe('vehicles');
if (subscription.ready()) {
const cars = Vehicles.findOne({name: 'Jack'}); //<<<<<<<<<<<<<<< document OK.
onData(null, { cars });
}
};
const Container = composeWithTracker(composer)(Info);
ReactDOM.render(<Container />, document.getElementById('react-info'));

Resources