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;
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>
}
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With