最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - ReactJsRedux Invariant Violation: Could not find "store" in either the context or props of &q

programmeradmin3浏览0评论

Not sure why I'm getting this error, it happened when I added connect from redux to my Login ponent, so I could connect my store.

FAIL src/ponents/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 ponent in a <Provider>, or explicitly pass "store" as a prop to "Connect(LoginContainer)".

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, bineReducers, pose, 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 = bineReducers({
        user: userReducer,
        user: authReducer
    });

    //noinspection JSUnresolvedVariable
    const store = createStore(reducers,
        pose(
            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 './ponents/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="/" ponent={ LoginContainer }/>
            <Route path="/dashboard" ponent={ Dashboard }/>
            <Route ponent={ 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 '../mon/Notification'

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

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

    it('should render the Notification ponent 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?

Not sure why I'm getting this error, it happened when I added connect from redux to my Login ponent, so I could connect my store.

FAIL src/ponents/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 ponent in a <Provider>, or explicitly pass "store" as a prop to "Connect(LoginContainer)".

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, bineReducers, pose, 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 = bineReducers({
        user: userReducer,
        user: authReducer
    });

    //noinspection JSUnresolvedVariable
    const store = createStore(reducers,
        pose(
            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 './ponents/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="/" ponent={ LoginContainer }/>
            <Route path="/dashboard" ponent={ Dashboard }/>
            <Route ponent={ 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 '../mon/Notification'

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

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

    it('should render the Notification ponent 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?

Share Improve this question edited Jun 12, 2017 at 14:11 Leon Gaban asked Jun 9, 2017 at 13:05 Leon GabanLeon Gaban 39k122 gold badges348 silver badges550 bronze badges 7
  • where is createCommonStore code, – Shubham Khatri Commented Jun 9, 2017 at 13:06
  • @ShubhamKhatri just added it sorry! store.js – Leon Gaban Commented Jun 9, 2017 at 13:11
  • in your index.js do a console.log() to see if you get the store defiend – Shubham Khatri Commented Jun 9, 2017 at 13:17
  • 1 can you provide login.test.js file? – lavish Commented Jun 9, 2017 at 13:28
  • 1 How do you connect the store in the test? – Dave Newton Commented Jun 9, 2017 at 14:43
 |  Show 2 more ments

3 Answers 3

Reset to default 9 +100

Redux remends exporting the unconnected ponent 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 e from the store directly on the ponent.

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

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
};

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论