Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Invariant Violation: Could not find "store" in either the context or props of "Connect(App)"

Hello I'm trying to use redux in a react-native app and I get the error mentioned in the title, specifically Invariant Violation: Could not find "store" in either the context or props of "Connect(App)". Either wrap the root component in a <Provider>, or explicitly pass "store" as a prop to "Connect(App)". I'm not sure what is the problem and although I followed similar threads nothing worked so far.

Bellow is my App.js source

import React, { Component } from 'react';
import { Provider, connect } from 'react-redux';
import { Platform, StyleSheet, Text, View } from 'react-native';
import * as utils from './src/utils';
import store from './src/store';

const styles = StyleSheet.create({
    container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        backgroundColor: '#F5FCFF',
    },
    welcome: {
        fontSize: 20,
        textAlign: 'center',
        margin: 10,
    },
    instructions: {
        textAlign: 'center',
        color: '#333333',
        marginBottom: 5,
    },
});

class App extends React.Component {

    constructor(...args) {
        super(...args);
    }

    login({ username, password }) {

        let requestData = new FormData();

        requestData.append('username', username);
        requestData.append('password', password);

        console.log('[Login]: Attempt');
        console.log(JSON.stringify(requestData, null, 4));

        fetch('https://my-api.lab/user/login', {
            method: 'POST',
            body: requestData
        }).then(res => res.json()).then((json) => {

            console.log('[Login]: Response');
            console.log(JSON.stringify(json, null, 4));

            if (json.authenticated) {

                console.log('[Login]: Success');

                this.props.userLoginSuccess(json);

            } else {
                console.log('[Login]: Failure');
            }

        }).catch((err) => {

            console.log('[Login]: error');
            console.log(JSON.stringify(err, null, 4));

        });

    }

    componentDidMount() {

        console.log('App mounted!');

        this.login({
            username: 'asdf',
            password: 'asdf'
        });

    }

    render() {

        let username = Object.keys(this.props.user).length ? this.props.user.data.post.username : '';

        return (

            <Provider store={store}>

                <View style={styles.container}>
                    <Text style={styles.welcome}>
                        Welcome {username} to React Native!
                    </Text>
                </View>

            </Provider>

        );
    }
}

const mapStateToProps = (state) => {
    return {
        user: state.userReducer
    }
}

const mapDispatchToProps = (dispatch) => {

    return {

        userLoginSuccess: (user) => {

            dispatch({
                type: 'USER_LOGIN_SUCCESS',
                payload: user
            })

        },

        userLoginFailure: (user) => {

        }

    }

}

export default connect(mapStateToProps, mapDispatchToProps)(App);

Also here is my index.js file

import { AppRegistry } from 'react-native';
import App from './App';

AppRegistry.registerComponent('pushNotificationsNative', () => App);

The store

import { createStore } from 'redux';
import rootReducer from '../reducers/index.js';

const store = createStore(
    rootReducer
);

export default store;

and finally a simple reducer

import deepExtend from 'deep-extend';

const initialState = {};

const userReducer = (state = initialState, action) => {

    switch (action.type) {

        case 'USER_LOGIN_SUCCESS': {
            return deepExtend({}, state, action.payload);
        }

        default: {
            return deepExtend({}, state);
        }

    }

}

export default userReducer;
like image 991
0x_Anakin Avatar asked Apr 27 '18 13:04

0x_Anakin


3 Answers

You are trying to access the store in the App component whereas, your Provider resides inside it. So, App doesn't really get store from the context. You need to wrap App with Provider like

class App extends React.Component {

    constructor(...args) {
        super(...args);
    }

    login({ username, password }) {

        let requestData = new FormData();

        requestData.append('username', username);
        requestData.append('password', password);

        console.log('[Login]: Attempt');
        console.log(JSON.stringify(requestData, null, 4));

        fetch('https://my-api.lab/user/login', {
            method: 'POST',
            body: requestData
        }).then(res => res.json()).then((json) => {

            console.log('[Login]: Response');
            console.log(JSON.stringify(json, null, 4));

            if (json.authenticated) {

                console.log('[Login]: Success');

                this.props.userLoginSuccess(json);

            } else {
                console.log('[Login]: Failure');
            }

        }).catch((err) => {

            console.log('[Login]: error');
            console.log(JSON.stringify(err, null, 4));

        });

    }

    componentDidMount() {

        console.log('App mounted!');

        this.login({
            username: 'asdf',
            password: 'asdf'
        });

    }

    render() {

        let username = Object.keys(this.props.user).length ? this.props.user.data.post.username : '';

        return (
                <View style={styles.container}>
                    <Text style={styles.welcome}>
                        Welcome {username} to React Native!
                    </Text>
                </View>
        );
    }
}

const mapStateToProps = (state) => {
    return {
        user: state.userReducer
    }
}

const mapDispatchToProps = (dispatch) => {

    return {

        userLoginSuccess: (user) => {

            dispatch({
                type: 'USER_LOGIN_SUCCESS',
                payload: user
            })

        },

        userLoginFailure: (user) => {

        }

    }

}

const ConnectedApp = connect(mapStateToProps, mapDispatchToProps)(App);

export default const Root = () => {
    <Provider store={store}><ConnectedApp/></Provider>
}
like image 67
Shubham Khatri Avatar answered Nov 15 '22 08:11

Shubham Khatri


You have App wrapped in a connect() method but are instantiating the <Provider> component in App. My hunch is that at this point there is no store for connect to access and give to App. Try removing the connect statement and see if that resolves your issue.

You have code that depends on data from store, I would comment that out for the time being to see if the above solution works. If it does, make app just a simple component that stands up the store and move the code for doing login and checking user to a new component.

like image 39
callmetwan Avatar answered Nov 15 '22 10:11

callmetwan


Provider must wrap a higher-level component. Lower-level component, or nested component imported to the one with provider, should have the connect function.

Top-level(index.js):

AppRegistry.registerComponent(appName, () => App);

Higher-level component(app.js):

const store = createStore(reducers);
const App = () => {
    return <Provider store={store}>
        <UI/>
    </Provider>;
};
export default App;

Lower-level/nested(ui.js)

const UI = () => {
return <View><Text>Hello</Text></View>;
};
export default connect()(UI);

This issue is specific to react-native, because in react, we typically wrap App within index.js, which is not the case for RN.

like image 27
max3d Avatar answered Nov 15 '22 09:11

max3d