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?

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

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.

Method 2

You need to pass store as either a prop or context in your test. mount method accepts context as another parameter.

and how do you get store here? You create store the same way you created in app.js

Method 3

You could use React’s contextType or pass propType. You would need to declare it either as a prop or contextType.

Provider.contextTypes = {
  Store: React.PropTypes.object.isRequired
};

 Provider.propTypes= {
  Store: React.PropTypes.object.isRequired
};


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x