I have 2 groups of screens in my app.
1) AuthorizedScreens
2) NotAuthorizedScreens
As soon as the app loads I want to check if the user is logged in or not? If the user is logged in, the app loads AuthorizedScreens and if not, It loads NotAuthorizedScreens. How do i achieve this? I have included a sample of not working code but I guess that's how it could be!
App.js
import React from 'react';
import { AsyncStorage } from 'react-native';
import { Provider } from 'react-redux';
import { DrawerNavigator, StackNavigator } from 'react-navigation';
import store from './store';
export default class App extends React.Component {
async componentWillMount() {
const token = await AsyncStorage.getItem('facebook_token');
}
render() {
const AuthorizedScreens = DrawerNavigator(...
const NotAuthorizedScreens = DrawerNavigator(...
return (
<Provider store={store}>
{ (this.token) ? <AuthorizedScreens /> : <NotAuthorizedScreens /> }
</Provider>
);
}
}
Note!
I have an action creator that checks if the user is logged in. But I couldn't connect it to the App component, therefore I decided to use AsyncStorage to store a facebook_token, and if the token exist means user is logged in and if not the user is not...
"dependencies": {
"expo": "^20.0.0",
"react": "16.0.0-alpha.12",
"react-native": "https://github.com/expo/react-native/archive/sdk-20.0.0.tar.gz",
"react-navigation": "^1.0.0-beta.11",
"react-redux": "^5.0.6",
"redux": "^3.7.2",
"redux-thunk": "^2.2.0"
}
I have solved this issue by setting a state in componentWillMount and calling it from return.
import React from 'react';
import { AsyncStorage } from 'react-native';
import { Provider } from 'react-redux';
import { DrawerNavigator, StackNavigator } from 'react-navigation';
import store from './store';
export default class App extends React.Component {
state = {
login_token: '',
};
async componentWillMount() {
const token = await AsyncStorage.getItem('facebook_token');
this.setState({ login_token: token });
}
render() {
const AuthorizedScreens = DrawerNavigator(...
const NotAuthorizedScreens = DrawerNavigator(...
return (
<Provider store={store}>
{ (this.state.login_token) ? <AuthorizedScreens /> : <NotAuthorizedScreens /> }
</Provider>
);
}
}
I have solved this problem, but I am not sure if I did it the right way! Because, Redux is in charge of my state management, but I manually used the react state management by setting state.
I will appreciate your inputs if you know a better way.
Related
Supposedly, I've got a small problem, but can't tackle it.
I've got a small React-Native app, one screen only.
Redux is used as a store. It's being built via Expo. While using connect of react-redux, I've got the following error:
Invariant Violation: Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.
Check the render method of Home.
The app works if the component rendered by Home component isn't wrapped by connect().
Attach some code below.
App.js
import React from "react";
import { createStackNavigator, createAppContainer } from 'react-navigation';
import Home from './src/pages/Home';
import { Provider } from 'react-redux';
import configureStore from './src/stores/store';
const { store } = configureStore();
function App() {
return (
<Provider store={store}>
<AppContainer />
</Provider>
)
}
const MainNavigator = createStackNavigator({
Home: { screen: Home }
},
{
headerMode: 'none',
navigationOptions: {
headerVisible: false,
}
}
);
const AppContainer = createAppContainer(MainNavigator);
export default App;
Home.js
import React from "react";
import { StyleSheet, View, Text, Dimensions } from "react-native";
import SwitchEventTypes from "../components/SwitchEventTypes";
class Home extends React.Component {
constructor() {
super();
}
render() {
return (
<View>
<SwitchEventTypes />
</View>
)
}
}
SwitchEventTypes.js
import React, { Component } from "react";
import { connect } from 'react-redux';
import { StyleSheet, View, Text, Dimensions, TouchableOpacity } from "react-native";
import { updateEventType } from '../actions/actions';
const mapDispatchToProps = (dispatch) => {
return {
updateEventType: (newEventType) => {
dispatch(updateEventType(newEventType));
}
};
};
const mapStateToProps = (state) => {
return {
eventType: state.filter.eventType,
};
};
class SwitchEventTypes extends React.Component {
constructor() {
super();
this.state = {
isSwitchEventTypeOn: true
}
this.handleEventTypeChange = this.handleEventTypeChange.bind(this);
}
handleEventTypeChange(newEventType) {
this.props.updateEventType(newEventType);
}
render() {
return (
<View style={styles.switchTypesContainer}>
{this.props.eventType === 'active' ? <Text>123</Text> :
<Text>456</Text>
}
</View>
)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(SwitchEventTypes);
store.js
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from '../reducers';
const middleware = [thunk];
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const composedEnhancers = composeEnhancers(
applyMiddleware(...middleware),
)
export default () => {
const store = createStore(rootReducer, composedEnhancers);
return { store };
};
package.json
"dependencies": {
"expo": "^32.0.6",
"expo-react-native-shadow": "^1.0.3",
"expo-svg-uri": "^1.0.1",
"prop-types": "^15.7.2",
"react": "16.8.6",
"react-native": "https://github.com/expo/react-native/archive/sdk-32.0.1.tar.gz",
"react-native-calendars": "^1.32.0",
"react-native-svg": "^9.4.0",
"react-navigation": "^3.9.1",
"react-redux": "^7.0.1",
"redux": "^4.0.1",
"redux-thunk": "^2.3.0",
"react-native-switch": "^1.5.0"
},
"devDependencies": {
"babel-preset-expo": "^5.0.0",
"redux-devtools-extension": "^2.13.8",
"schedule": "^0.4.0"
},
What may be the matter? Please, help. Thanks.
Your mapDispatchToProps is returning an object. You also don't need the extra returns as fat arrow functions already have an implicit return. Will make your code a little more readable.
const mapDispatchToProps = dispatch => ({
updateEventType: (newEventType) => dispatch(updateEventType(newEventType));
});
const mapStateToProps = state => {
eventType: state.filter.eventType, // might be worth your time to investigate selectors down the road
};
I am getting the error "(0, _reactnavigation.createStackNavigator)" when trying to run a simple stackNavigator.....could someone please help?
the error is occurring at the line const AppStackNavigator = createStackNavigator(....its showing a value of zero
1. simple login screen
import { View, Text, Card, Button } from 'react-native';
import React from 'react';
class LoginScreen extends React.Component {
render() {
return (
<View>
<Text>Hello Login</Text>
</View>
);
}
}
export default LoginScreen;
2. App.js
import { createStackNavigator } from 'react-navigation';
class App extends React.Component {
render() {
return (
<View>
<AppStackNavigator />
</View>
);
}
}
const AppStackNavigator = createStackNavigator({
Login: { screen: LoginScreen },
});
export default App;
3. package.json
"react": "16.3.1",
"react-native": "^0.55.4",
"react-navigation": "^2.5.5",
It looks like you forgot to import the LoginScreen to the App.jsfile. Hope it helps, cheers.
I have been using React Native for a few years and have only recently needed to utilise Redux on a new, more complex project. I am currently in the process of following a number of tutorials trying to work my way through the basics.
I am currently stuck with the following error:
Invariant Vilation: Could not find "store" in either the context of props of "Connect(App)"
I have found a number of posts with information about this error but because of the low amount of knowledge I currently have, I am unsure as to how to correctly implement a fix.
This project was created with create-react-native-app and I am using the Expo app to test.
In my eyes this should work because the root element of App is a Provider element passing the store as a prop which seems to contradict what the error is saying.
configureStore.js
import { createStore, applyMiddleware } from 'redux';
import app from './reducers';
import thunk from 'redux-thunk';
export default function configureStore() {
return createStore(app, applyMiddleware(thunk));
}
App.js:
import React from 'react';
import { Text } from 'react-native';
import { Provider, connect } from 'react-redux';
import configureStore from './configureStore';
import fetchPeopleFromAPI from './actions';
const store = configureStore();
export class App extends React.Component {
componentDidMount() {
props.getPeople()
}
render() {
const { people, isFetching } = props.people;
return (
<Provider store={store}>
<Text>Hello</Text>
</Provider>
);
}
}
function mapStateToProps(state) {
return {
people: state.people
}
}
function mapDispatchToProps(dispatch) {
return {
getPeople: () => dispatch(fetchPeopleFromAPI())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
You are trying to access the store in the App component even before it has been passed. Therefore it is not able to find the store.
You need to make a separate component and connect that using react-redux such as
<Provider store={store}>
<ConnectedComponent />
</Provider>
...
class ConnectedComponent extends React.Component {
componentDidMount () {
this.props.getPeople()
}
render() {
return (
<View>
<Text> ... </Text>
</View>
)
}
}
function mapStateToProps(state) {
return {
people: state.people
}
}
function mapDispatchToProps(dispatch) {
return {
getPeople: () => dispatch(fetchPeopleFromAPI())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ConnectedComponent);
How to fix this error ?
Could not find "store " in either the context or props of "Connect(Home)". Either wrap the root component in a or explicitly pass "store" as a prop to "Connect(Home)".
import React, { Component, PropTypes } from "react";
import { Router } from "react-native-router-flux";
import scenes from "../routes/scenes";
import { Provider } from "react-redux";
export default class AppContainer extends Component {
static propTypes = {
store: PropTypes.object.isRequired
}
render(){
return (
<Provider store={this.props.store}>
<Router scenes={scenes} />
</Provider>
);
}
}
From the commments:
import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
import makeRootReducer from "./reducers";
import { createLogger } from "redux-logger";
const log = createLogger({ diff: true, collapsed: true });
const store = createStore( makeRootReducer(), initialState, compose(applyMiddleware(...middleware), ...enhancers ));
return store;
};
This is an example of how this should be connected.
Provider Class
// Third party.
import React, { Component } from 'react';
import { Provider } from 'react-redux';
// App modules
import setupStore from './utils/setupStore'; //This is the trick
// Assets Actions
const store = setupStore(); //==> Here calling the function inside setupStore file
// App component.
import App from './App';
export default class Root extends Component {
render() {
return (
<Provider store={store}>
<App />
</Provider>
)
}
}
./utils/setupStore File.
// Node Core Third party
import { applyMiddleware, createStore } from 'redux';
import thunk from 'redux-thunk';
// App Modules
import rootReducer from '../store'; //Your combine reducer file
const middleware = applyMiddleware(thunk);
export default function configureStore() {
const store = createStore(rootReducer, middleware); //This is the createStore I was talking to you about.
if (module.hot) {
module.hot.accept(() => {
const nextRootReducer = require('../store/index').default;
store.replaceReducer(nextRootReducer);
})
}
return store;
}
if you're using react-router you need to follow this order:
index.jsx --
import { Router, Route, IndexRoute, browserHistory } from 'react-router'
ReactDOM.render(
<Provider store={store}>
<div>
<Router history={browserHistory}>
<Route path="/" component={App}>
<Route path="foo" component={Foo}/>
<Route path="bar" component={App}/>
</Route>
</Router>
</div>
</Provider>,
document.getElementById('react-app')
)
package.json --
"react": "^0.14.7",
"react-dom": "^0.14.7",
"react-redux": "^4.3.0",
"react-router": "^2.0.0",
"redux": "^3.2.1",
"react-router-redux": "^4.0.0",
"webpack": "^1.9.8",
"webpack-bundle-tracker": "0.0.5",
"webpack-dev-server": "^1.16.5"
"Element type is invalid: expected a string (for built-in components)
or a class/function (for composite components but got: object. Check
the render method of AppContainer.
I am getting the error that was initially shown in a tutorial video here:
https://youtu.be/6zVGVHWklg8?t=6m1s
The file has been named correctly "/app/containers/ApplicationTabs/index.ios.js" and the file contents are exactly as shown in the tutorial.
However where the app worked in the video once ApplicationTabs was "connected" I am still getting the above error after doing so myself.
I am using a newer version of a lot of the react/react-native/redux modules though. I am not sure if the convention of using files with OS specific naming would affect the way they have to be imported?
Below is my package.json dependencies :
"dependencies": {
"react": "15.4.2",
"react-native": "0.40.0",
"react-redux": "^5.0.2",
"redux": "^3.6.0",
"redux-logger": "^2.7.4",
"redux-thunk": "^2.2.0"
}
This is the contents of "/app/containers/ApplicationTabs/index.ios.js" :
import { View, TabBarIOS, TabBarItemIOS } from 'react-native';
import React, { Component } from 'react';
import { connect } from 'react-redux';
class ApplicationTabs extends Component {
render() {
return ( <View /> );
}
}
function mapStateToProps(state) {
return { };
}
export default connect(mapStateToProps)(ApplicationTabs);
This is the contents of "/app/containers/AppContainer.js" :
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {ActionCreators} from '../actions';
import {bindActionCreators} from 'redux';
import ApplicationTabs from './ApplicationTabs';
class AppContainer extends Component {
render() {
return ( <ApplicationTabs { ...this.props } /> )
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(ActionCreators, dispatch);
}
export default connect( () => { return {} }, mapDispatchToProps)(AppContainer);
Please check the react-redux tutorial at: http://redux.js.org/docs/basics/UsageWithReact.html Maybe this will help.
import {connect} from 'react-redux';
import {ActionCreators} from '../actions';
import {bindActionCreators} from 'redux';
import ApplicationTabs from './ApplicationTabs';
function mapDispatchToProps(dispatch) {
return bindActionCreators(ActionCreators, dispatch);
}
const AppContainer = connect( null, mapDispatchToProps)(ApplicationTabs);
export default AppContainer;
The container is represented by the connect function and you tell it which component it should contain.