Using Redux with React Native - react-native

I am working on a react-native app created using: create-react-native-app
As far as I can tell the most top level component is inside the App.js file and I have imported the Provider there and wrapped it around the Top Level Component but I am still getting the following errors for some reason:
ExceptionsManager.js:65 Invariant Violation: Could not find "store" in
either the context or props of "Connect(App)". Either wrap the root
component in a , or explicitly pass "store" as a prop to
"Connect(App)".
What am I doing wrong?
Here is my code:
import React from 'react';
import { StyleSheet, Text, View, FlatList, TextInput, StatusBar, Button,TouchableOpacity } from 'react-native';
import { TabNavigator, StackNavigator } from 'react-navigation';
import { Constants } from 'expo'
import { purple, white } from './utils/colors'
import { saveDeckTitle, getDecks, getDeck, addCardToDeck } from './utils/api'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import reducer from './reducers'
import { connect } from 'react-redux'
import Decks from './components/Decks'
import NewQuestionView from './components/NewQuestionView'
import NewDeck from './components/NewDeck'
const R = require('ramda')
const store = createStore(reducer)
const DecksETC = StackNavigator({
Decks: {
screen: Decks
},
NewDeck: {
screen: NewDeck
},
NewQuestionView: {
screen: NewQuestionView
}
})
const NewDeckETC = StackNavigator({
NewDeck: {
screen: NewDeck
},
Decks: {
screen: Decks
},
NewQuestionView: {
screen: NewQuestionView
}
})
const Tabs = TabNavigator({
DecksETC: {
screen: DecksETC
},
NewDeckETC: {
screen: NewDeckETC
}
});
class App extends React.Component {
render() {
console.log('R', R)
return (
<Provider store={store}>
<Tabs />
</Provider>
// <Tabs />
);
}
}
const styles = StyleSheet.create({
container: {
paddingTop: 23,
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
input: {
margin: 15,
height: 40,
borderColor: '#7a42f4',
borderWidth: 1
},
});
function mapStateToProps(state) {
console.log('mapStateToProps')
debugger
return {
'sample': 'sample'
}
}
export default connect(mapStateToProps)(App)

The problem is that App component does not know anything about the store because the Provider component is what brings the Redux store into its children components. The App component itself does not receive a reference to the store, so when you try to connect, the store is not found.

Well I see you have use connect function for the root component directly, which is a pattern I never see before. Let's try the normal way, which is that you will create a Root component and just use it inside Provider. Then you will pass child components into that Root component.
You will then separate each child component into a new file. In each file, you will use connect() to pass redux store into that component. That's the common pattern I see in many many projects. And this pattern will help you avoid confusing situation like above!

Well the function connect requires two functions mapStateToProps and mapDispatchToProps as arguments
i.e. export default connect(mapStateToProps,mapDispatchToProps)(App)
if you do not have a mapDispatchToProps then simply pass null.
i.e. export default connect(mapStateToProps,null)(App)

Related

Navigator Trouble

Good afternoon, actually i m keeping error while try to navigate.
This is my App.js
import { createStackNavigator, createAppContainer } from "react-navigation";
import Login from './src/Login';
import NuovoAccount from './src/NuovoAccount';
import HomePage from './src/HomePage';
import Lista_Sveglie from './src/Lista_Sveglie';
import NuovoAccount_2 from './src/NuovoAccount_2';
import Nuova_sveglia from './src/Nuova_sveglia';
import ScreenSveglia from './src/ScreenSveglia';
import Registra from './src/Registra';
import temp from './src/temp';
import Account from './src/Menu/Account';
import ElencoUtenti from './src/Menu/ElencoUtenti';
import MenuImpostazioni from './src/Menu/MenuImpostazioni';
import SelUtenteDest from './src/Menu/SelUtenteDest'
import Accedi from './src/Accedi'
import Landing from './Landing'
const AppNavigator = createStackNavigator(
{
Home: Landing,
Accedi : Accedi ,
Login: Login,
NuovoAccount: NuovoAccount,
HomePage: HomePage,
Lista_Sveglie: Lista_Sveglie,
NuovoAccount_2: NuovoAccount_2,
Nuova_sveglia: Nuova_sveglia,
ScreenSveglia: ScreenSveglia,
Registra: Registra,
temp: temp,
MenuImpostazioni: MenuImpostazioni,
ElencoUtenti: ElencoUtenti,
Account: Account,
SelUtenteDest: SelUtenteDest,
},
{
initialRouteName: "Home",
defaultNavigationOptions: {
headerTintColor: '#ccc',
headerStyle: {
borderBottomWidth: 4,
borderBottomColor: '#80ba27',
backgroundColor: '#364054',
},
headerTitleStyle: {
textAlign: 'center',
flex: 1
},
}
}
);
export default createAppContainer(AppNavigator)
and here my Landing.js
import React, { Component } from 'react';
import { StyleSheet, Text, View, Alert, Button } from 'react-native';
import * as firebase from 'firebase';
import HomePage from './src/HomePage';
import Accedi from './src/Accedi'
import { createStackNavigator, createAppContainer } from "react-navigation";
export default class Landing extends Component {
constructor() {
super();
this.state = {
loggedIn: false,
}
}
componentWillMount() {
console.log("QUI ENTRA")
---some firebase config ...
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
firebase.auth().onAuthStateChanged((user) => {
if (user) {
this.setState({ loggedIn: true })
} else {
this.setState({ loggedIn: false })
}
})
}
render(){
if (this.state.loggedIn){
return <HomePage/>
}else{
return <Accedi/>
}
}
}
Here my HomePage
<View>
<Text
onPress={() => this.props.navigation.navigate('MenuImpostazioni')}
style={style.footer_text_icone}>Configura Impostazioni</Text>
</View>
Can anyone tell me how to fix it and explain why this error ?
from documentation i saw this.props. is created on createnavigator function
"undefined is not an object - this.prop.navigation.navigate"
The problem here is that your HomePage component isn't a direct child of your stackNavigator. You need to pass it trough props to your child component doing:
render(){
if (this.state.loggedIn){
return <HomePage navigation={this.props.navigation}/>
}else{
return <Accedi/>
}
}
Inside the render function of your Landing.js
Then your child component will have access to it.
This is happening because you are using the same component, both as screen that as child component.
{
Home: Landing, //Here (as child)
Accedi : Accedi ,
Login: Login,
NuovoAccount: NuovoAccount,
HomePage: HomePage, //Here
Lista_Sveglie: Lista_Sveglie,
NuovoAccount_2: NuovoAccount_2,
Nuova_sveglia: Nuova_sveglia,
ScreenSveglia: ScreenSveglia,
Registra: Registra,
temp: temp,
MenuImpostazioni: MenuImpostazioni,
ElencoUtenti: ElencoUtenti,
Account: Account,
SelUtenteDest: SelUtenteDest,
}
react-navigation doesn't give access to navigation to a component just because it has been declared inside of it. By doing that if/else you are actually creating a whole new instance of that same component, with the only difference that it do not have access to this.props.navigation.
Landing.js isn't a screen that is defined within your stack navigator, so it does not have access to this.props.navigation.
If you don't want Landing.js within the stack navigator, you can follow this tutorial to use navigate and other navigation actions from any component: https://reactnavigation.org/docs/en/navigating-without-navigation-prop.html

implementing redux in react native

I'm trying to implement redux to show balance in multiple screens as I update balance in single screen it should reflect in all other screens/components.
I'm pretty new to redux. As you know with complexity around redux, its even making difficult to implement it.
I followed some examples in GitHub and youtube and started implementing it .
under Actions folder I have. following two files
counteractions.js
import * as types from './actionTypes.js';
//ActionCreator methods
export function updateBalance(balanceInfo) {
return {
type: types.LEDGER_BALANCE,
payLoad: { balanceInfo }
}
}
Under Reducers folder.I have this file
balance.js
import * as types from '../actions/actionTypes.js';
const initialState = {
balance: 0
}
// reducer functions .. accepts current/initial state , actions and returns new state
const balanceReducer=(state,action)=>
{
switch (action.type) {
case types.LEDGER_BALANCE:
return {
balance: action.payload.balanceInfo
}
break;
default:
break;
}
}
export default balanceReducer;
in ConfigureStore.js
import {createStore} from 'redux';
import rootReducer from './reducers/index.js';
import balanceReducer from './reducers/balance.js';
const initailState = {
balance: 0,
}
export const store=createStore(balanceReducer,balanceReducer);
App.js
/**
* Sample React Native App
* https://github.com/facebook/react-native
* #flow
*/
import React, { Component } from 'react';
import { Provider } from 'react-redux';
//Provider - makes redux store available to connect() class in component hierarchy below
import { applyMiddleware, createStore, compose, combineReducers } from "redux";
import thunkMiddleware from 'redux-thunk';
import createLogger from 'redux-logger';
import rootReducer from './reducers/index.js';
//import store from './configureStore.js';
import {
Platform,
StyleSheet,
Text,
View,
TouchableOpacity,
TextInput
} from 'react-native';
import ReduxDemo from "./reduxDemo.js";
import { store, reducer } from './balanceDemo.js';
const instructions = Platform.select({
ios: 'Press Cmd+R to reload,\n' +
'Cmd+D or shake for dev menu',
android: 'Double tap R on your keyboard to reload,\n' +
'Shake or press menu button for dev menu',
});
export default class App extends Component<{}> {
constructor(props) {
super(props);
this.state = {
balancelocal: '',
}
}
_updateLedger = () => {
// store.dispatch({ type: 'BALANCE', payLoad: '500' });
store.dispatch({ type: 'BALANCE', payLoad: 'Your balance is 8000 MUR' });
}
render() {
store.subscribe(() => {
this.setState({
balancelocal: store.getState(),
})
//this.balanceInfo = store.getState().balance;
// alert(this.state.balancelocal);
});
return (
<View style={styles.container}>
<TouchableOpacity onPress={this._updateLedger}>
<Text>Update balance</Text>
</TouchableOpacity>
<TextInput style={{height:100,width:400}} value={this.state.balancelocal}/>
</View>
);
}
}
The styling for it
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,
},
});
I'm yet to complete configure store file. and. I'm wondering, where I have to subscribe and dispatch actions ...
I want to update balance with button click from app.js
I have. to update balance in another page automatically..
Please guide me to understand and implement redux .Please suggest better folder structure and better way to implement redux.
https://redux.js.org/basics/exampletodolist
The above link has a basic setting up of react with redux. It is almost similar for React Native too.
You need to wrap your App component inside Provider imported from 'react-redux', and then give that to AppRegistry. Also you do not seem to have imported any actions and haven't used the connect function either. Like the comment above, it is better for you go through a video guide on basics of redux. It'll help you understand all the complexity, and once you understand, nothing is as easy as redux. All the best.

Redux error in react-native

I'm trying to implement redux to show balance in multiple screens as I update balance in single screen it should reflect in all other screens/components.
I'm pretty new to redux. As you know with complexity around redux, its even making difficult to implement it.
I followed some examples in GitHub and youtube and started implementing it .
Under Actions folder I have. following two files
counteractions.js
import * as types from './actionTypes.js';
//ActionCreator methods
export function updateBalance(balanceInfo) {
return {
type: types.LEDGER_BALANCE,
payLoad: { balanceInfo }
}
}
Under Reducers folder.I have this file
balance.js
import * as types from '../actions/actionTypes.js';
const initialState = {
balance: 0
}
// reducer functions .. accepts current/initial state , actions and returns new state
const balanceReducer=(state,action)=>
{
switch (action.type) {
case types.LEDGER_BALANCE:
return {
balance: action.payload.balanceInfo
}
break;
default:
break;
}
}
export default balanceReducer;
in ConfigureStore.js
import {createStore} from 'redux';
import rootReducer from './reducers/index.js';
import balanceReducer from './reducers/balance.js';
const initailState = {
balance: 0,
}
export const store=createStore(balanceReducer,balanceReducer);
App.js
/**
* Sample React Native App
* https://github.com/facebook/react-native
* #flow
*/
import React, { Component } from 'react';
import { Provider } from 'react-redux';
//Provider - makes redux store available to connect() class in component hierarchy below
import { applyMiddleware, createStore, compose, combineReducers } from "redux";
import thunkMiddleware from 'redux-thunk';
import createLogger from 'redux-logger';
import rootReducer from './reducers/index.js';
//import store from './configureStore.js';
import {
Platform,
StyleSheet,
Text,
View,
TouchableOpacity,
TextInput
} from 'react-native';
import ReduxDemo from "./reduxDemo.js";
import { store, reducer } from './balanceDemo.js';
const instructions = Platform.select({
ios: 'Press Cmd+R to reload,\n' +
'Cmd+D or shake for dev menu',
android: 'Double tap R on your keyboard to reload,\n' +
'Shake or press menu button for dev menu',
});
export default class App extends Component<{}> {
constructor(props) {
super(props);
this.state = {
balancelocal: '',
}
}
_updateLedger = () => {
// store.dispatch({ type: 'BALANCE', payLoad: '500' });
store.dispatch({ type: 'BALANCE', payLoad: 'Your balance is 8000 MUR' });
}
render() {
store.subscribe(() => {
this.setState({
balancelocal: store.getState(),
})
//this.balanceInfo = store.getState().balance;
// alert(this.state.balancelocal);
});
return (
<View style={styles.container}>
<TouchableOpacity onPress={this._updateLedger}>
<Text>Update balance</Text>
</TouchableOpacity>
<TextInput style={{height:100,width:400}} value={this.state.balancelocal}/>
</View>
);
}
}
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,
},
});
I'm yet to complete configure store file. and. I'm wondering. where I have to subscribe and dispatch actions ..
I want to update balance with button click from app.js
I have. to update balance in another page automatically..
Please guide me to understand and implement redux .Please suggest better folder structure and better way to implement redux.
There is quite a bit here to understand.
The basic workflow is (you can have the receiving component as a different one)
Component Button > Action > Reducer > Component Props > Render
To achieve this you need both the setup of the store and the invoking of the "event" through redux.
Below is an example (excuse if not perfect, just typed into here), but the way the other component gets the value from the action is becuase it uses the 'connect' HOC. Everytime redux gets a state change it calls all components that are 'connected'. Here we take the updated balance and return it as part of the 'mapStateToProps' function, which is just setting the components props with that object. The balance is then accessed via this.props.balance and displayed.
This becomes more useful if you want to either call an api in the action and store the result in the reducer. All connected components will then get that new data.
Note1: I have only used redux-thunk middleware to dispatch so forgive me for using that.
Note2: This is a simple example. When the app gets better you will need to prevent over-rendering since any reducer changes will invoke all connected components. I use reselect here.
Setup
reducers.js
import { combineReducers } from 'redux';
import { balanceReducer } from 'balanceReducer';
export default combineReducers({
balanceReducer
})
store.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk'
import combineReducers from './reducers'
export default function configureStore() {
let store = createStore(combineReducers, applyMiddleware(thunk));
return store;
}
index.js
import React, { Component } from 'react';
import { AppRegistry, View } from 'react-native';
import { Provider } from 'react-redux';
import configureStore from './store';
import Component1 from './component1';
const store = configureStore()
const myapp = () => (
<Provider store={store}>
<View>
<Component1 />
<View>
</Provider>
)
AppRegistry.registerComponent('myapp', () => myapp);
Components
component1.js (key part is the connect HOC)
import { connect } from 'react-redux';
import React, { Component } from 'react';
import { Text, View, TouchableOpacity } from 'react-native';
import { updateBalance } from './action';
class Component1 extends Component {
_updateLedger = () => {
this.props.updateBalance(500);
}
render() {
const { balance } = this.props;
return (
<View>
<TouchableOpacity onPress={this._updateLedger}>
<Text>Update balance</Text>
</TouchableOpacity>
<Text>{balance}</Text>
</View>
)
}
}
function mapStateToProps(state) {
return {
balance: state.balanceReducer.balance
}
}
function mapDispatchToProps(dispatch) {
return {
updateBalance = (balanceInfo) => dispatch(updateBalance(balanceInfo))
};
}
export default connect(
mapStatetoProps,
mapDispatchToProps
)(Component1)
action.js
export function updateBalance(balanceInfo) {
return {
type: types.LEDGER_BALANCE,
payLoad: { balanceInfo }
}
}
balanceReducer.js (key part here is to return new state)
const initialState = {
balance: 0,
}
export function balanceReducer(state = initialState, action) {
if(action.type === types.LEDGER_BALANCE) {
return {
balance: action.payLoad.balanceInfo
}
}
}

Why isn't my component's state computed property updating after Redux store updates its value?

Why isn't my component's state computed property updating after Redux store updates its value?
I am using some helper methods to grab the sub-store via AppStore.getState().ApiStore for my isAuthenticated state property. It seems like when this store value updates, the component state value does not update. Does React Native not watch for store updates in computed component state properties?
My component looks like the below:
// Vendor
import React, { Component } from 'react'
import { AppRegistry, Text, View, StyleSheet, TextInput, Button} from 'react-native'
import AppStore from './Stores/AppStore'
import StoreHelpers from './Stores/StoreHelpers'
// Custom
import Login from './Components/Login/Login'
import Api from './Services/Api.js'
// Styles
const styles = StyleSheet.create({
mainView: {
flex: 1,
padding: 20,
marginTop: 30,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#3b5998',
},
});
// Main App Component
export default class Main extends Component {
constructor(props) {
super(props)
this.state = {
isLoading: false,
isAuthenticated: !StoreHelpers.getApiStore().userBalanceResponse.error // Computed property from store
}
// Enable this for debugging
console.log(this.state)
AppStore.subscribe(() => {
console.log(AppStore.getState())
})
}
render() {
return (
<View style={styles.mainView}>
<Login />
</View>
)
}
}
// skip this line if using Create React Native App
// AppRegistry.registerComponent('AwesomeProject', () => Main);
You can't see it because your component is not subscribed to the store. Anything to do with store is the job of redux and NOT React Native. So if you wrap your component inside react-redux connect and pass in mapStateToProps to it you should get the right computed value.
// ... rest of imports
import { connect } from 'react-redux';
// Main App Component
class Main extends Component {
constructor(props) {
super(props)
this.state = {
isLoading: false,
isAuthenticated: this.props.isAuthenticated,
}
// ... rest of code
}
// ... rest of code
}
const mapStateToProps = (store) => ({
isAuthenticated: !userBalanceResponse: store.userBalanceResponse.error,
});
export default connect(mapStateToProps, null)(Main);
To make it work, make sure that you set up redux store properly. Wrap your root component within a Provider component and pass in store into it. Suppose your root component is called App, then it would look something like the following:
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import Main from 'path-to-main/Main';
// we will pass this store to the Provider
const store = createStore(
reducer,
// ... middlewares etc this is optional
);
export default class App extends Component {
render() {
return(
<Provider store={store}>
<Main />
</Provider>
)
}
}

Getting "Actions may not have an undefined "type""

I'm getting the error:
Actions may not have an undefined "type" property.
But I'm sure I defined it and spelled it right.
App:
import React, {Component} from 'react';
import { createStore, applyMiddleware, combineReducers } from 'redux';
import { Provider } from 'react-redux';
import { AsyncStorage } from 'react-native';
import thunk from 'redux-thunk';
import {persistStore, autoRehydrate} from 'redux-persist';
import FBLoginView from '../components/FBLoginView'
import * as reducers from '../reducers';
import Routing from './Routing';
const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);
const reducer = combineReducers(reducers);
const store = createStoreWithMiddleware(reducer, undefined, autoRehydrate());
persistStore(store, {
storage: AsyncStorage,
}, () => {
})
export default class App extends Component {
render() {
return (
<Provider store={store}>
<Routing />
</Provider>
);
}
}
Actions:
import * as types from './actionTypes';
export function getFacebookUser(user) {
return {
type: types.GET_FACEBOOK_USER,
user: user,
};
}
Types:
export const GET_FACEBOOK_USER = 'GET_FACEBOOK_USER';
Reducer:
import * as types from '../actions/actionTypes';
const initialState = {
user: {},
};
export default function reducer(state = initialState, action = {}) {
switch (action.type) {
case types.GET_FACEBOOK_USER:
return {
...state,
user: action.user
};
default:
return state;
}
}
Edit (My home.js page)
import React, { Component } from 'react'
import { View, Text, StyleSheet, Image, TouchableHighlight } from 'react-native'
import { Actions } from 'react-native-router-flux'
import {FBLogin, FBLoginManager} from 'react-native-facebook-login'
import FBLoginView from '../components/FBLoginView'
import * as facebookActions from '../actions/facebookActions';
import { connect } from 'react-redux'
import {bindActionCreators} from 'redux'
class Home extends Component {
constructor(props) {
super(props);
this.state = {
login: false
};
console.log(this.props)
}
render() {
let { facebook, actions } = this.props
_onLogin = (e) => {
actions.getFacebookUser(e.profile)
console.log(facebook)
}
_onLogout = (e) => {
console.log(e)
}
return (
<View style={styles.background}>
<Text>{this.state.login ? "Logged in" : "Logged out"}</Text>
<FBLogin
buttonView={<FBLoginView />}
ref={(fbLogin) => { this.fbLogin = fbLogin }}
loginBehavior={FBLoginManager.LoginBehaviors.Native}
permissions={["email","user_friends"]}
onLogin={function(e){_onLogin(e)}}
onLoginFound={function (e){console.log(e)}}
onLoginNotFound={function(e){console.log(e)}}
onLogout={function(e){_onLogin(e)}}
onCancel={function(e){console.log(e)}}
onError={function(e){console.log(e)}}
onPermissionsMissing={function(e){console.log(e)}}
style={styles.fbButton}
passProps={true}
/>
</View>
)
}
}
export default connect(store => ({
facebook: store.facebook.user,
}),
(dispatch) => ({
actions: bindActionCreators(facebookActions, dispatch)
})
)(Home);
const styles = StyleSheet.create({
background: {
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#00796B',
},
});
I don't think that you're dispatching the action correctly:
actions.getFacebookUser(e.profile)
is an action creator and will just return the action, not dispatch it.
I can't see your Home component that you're hooking up with Connect but I'd guess this is the source of events that you will want to dispatch as actions. Why not try dispatching directly against the store, and then move to use connect to hook up with mapDispatchToProps? Finally you can use bindActionCreators if this is necessary.
There are two very good (free) egghead.io courses that will help here, both by Dan Abramov:
https://egghead.io/courses/getting-started-with-redux
https://egghead.io/courses/building-react-applications-with-idiomatic-redux
and the docs are also very good, but I guess you've seen them.
After seeing more of the code, I can't see how the component you're connecting (Home) is linking its events (for example onLogin) to a dispatch property. I can see it caling its own internal function called _onLogin, but this just in turn call the action creator, it won't dispatch.
The connect function allows you connect properties on a component (here, Home) with the redux store; it effectively links, in your example, the 'onLogin' property of your Home component with a particular action and can then dispatch that action to the store.
So,your Home component needs to accept a property like 'onLogin' that it can then call; mapDispatchToProps is a function you write to marry up your child component's properties to dispatch actions. bindActionCreators is just a further helper to bind to action creators; it may be overkill in your current use case.
Dan Abramov explains this so much better than I can, so see the docs, but also see his answer here:
How to get simple dispatch from this.props using connect w/ Redux?