Leon Gaban Leon Gaban -4 years ago 308
React JSX Question

ReactJs/Redux Invariant Violation: Could not find "store" in either the context or props of "Connect(LoginContainer)"

Not sure why I'm getting this error, it happened when I added

connect
from
redux
to my Login component, so I could connect my
store
.


FAIL src/components/auth/Login.test.js

‚óŹ Test suite failed to run

Invariant Violation: Could not find "store" in either the context or props of "Connect(LoginContainer)". Either wrap the root component in a
<Provider>
, or explicitly pass "store" as a prop to "Connect(LoginContainer)".


enter image description here

Index.js



import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from "react-redux"
import { createCommonStore } from "./store";
import App from './App'
import css from './manage2.scss'

const store = createCommonStore();
const element = document.getElementById('manage2');
console.log("Index.js Default store", store.getState());

ReactDOM.render(
<Provider store={store}> // <-- store added here
<App />
</Provider>, element);


store.js



import React from "react"
import { applyMiddleware, combineReducers, compose, createStore} from "redux"
import thunk from "redux-thunk"
import { userReducer } from "./reducers/UserReducer"
import { authReducer } from "./reducers/AuthReducer"

export const createCommonStore = (trackStore=false) => {
const reducers = combineReducers({
user: userReducer,
user: authReducer
});

//noinspection JSUnresolvedVariable
const store = createStore(reducers,
compose(
applyMiddleware(thunk),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
);

if (trackStore) {
store.subscribe((() => {
console.log(" store changed", store.getState());
}));
}

return store;
};


App.js



import React from 'react'
import { BrowserRouter as Router } from 'react-router-dom'
import Routes from './components/Routes'
const supportsHistory = "pushState" in window.history

export default class App extends React.Component {
render() {
return (
<Router forceRefresh={!supportsHistory}>
<Routes />
</Router>
);
}
}


Routes.js



import React from 'react'
import { Route, Switch } from 'react-router-dom'
import LoginContainer from './auth/Login'
import Dashboard from './Dashboard'
import NoMatch from './NoMatch'

const Routes = () => {
return (
<Switch>
<Route exact={ true } path="/" component={ LoginContainer }/>
<Route path="/dashboard" component={ Dashboard }/>
<Route component={ NoMatch } />
</Switch>
);
}

export default Routes


Finally Login.js (code removed for brevity



import React from 'react'
import { connect } from "react-redux"
import { bindActionCreators } from 'redux';
import { setCurrentUser } from '../../actions/authActions'
import * as api from '../../services/api'

const mapDispatchToProps = (dispatch) => {
console.log('mapDispatchToProps', dispatch);
return {
setUser: (user) => {
bindActionCreators(setCurrentUser(user), dispatch)
}
}
}

class LoginContainer extends React.Component {
constructor(props) {
super(props)

this.state = {};

this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}

handleChange(e) {
}

handleSubmit(e) {
}

render() {
return (
<div className="app-bg">
...
</div>
)
}
}

export default connect(null, mapDispatchToProps)(LoginContainer);


Login.test



import React from 'react'
import ReactTestUtils from 'react-dom/test-utils'
import { mount, shallow } from 'enzyme'
import toJson from 'enzyme-to-json'
import { missingLogin } from '../../consts/errors'
import Login from './Login'
import Notification from '../common/Notification'

const loginComponent = shallow(<Login />);
const fakeEvent = { preventDefault: () => '' };

describe('<Login /> component', () => {
it('should render', () => {
const tree = toJson(loginComponent);
expect(tree).toMatchSnapshot();
});

it('should render the Notification component if state.error is true', () => {
loginComponent.setState({ error: true });
expect(loginComponent.find(Notification).length).toBe(1);
});
});

describe('User Login', () => {
it('should fail if no credentials are provided', () => {
expect(loginComponent.find('.form-login').length).toBe(1);
loginComponent.find('.form-login').simulate('submit', fakeEvent);
expect(loginComponent.find(Notification).length).toBe(1);
const notificationComponent = shallow(<Notification message={ missingLogin }/>);
expect(notificationComponent.text()).toEqual('Please fill out both username and password.');
});

it('input fields should be filled correctly', () => {
const credentials = { username: 'leongaban', password: 'testpass' };
expect(loginComponent.find('#input-auth-username').length).toBe(1);

const usernameInput = loginComponent.find('#input-auth-username');
usernameInput.value = credentials.username;
expect(usernameInput.value).toBe('leongaban');

const passwordInput = loginComponent.find('#input-auth-password');
passwordInput.value = credentials.password;
expect(passwordInput.value).toBe('testpass');
});
});


What do you see wrong here?

Answer Source

Redux recommends exporting the unconnected component for unit tests. See their docs.

In login.js:

// Named export for tests
export class LoginContainer extends React.Component {
}

// Default export
export default connect(null, mapDispatchToProps)(LoginContainer); 

And in your test:

// Import the named export, which has not gone through the connect function
import { LoginContainer as Login } from './Login';

You can then specify any props that would have come from the store directly on the component.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download