state undefined in react-native redux - react-native

I am implementing redux in react-native project. I have some asyn action and some pure actions. I am unable to get state value in my component. How do I get it.?
class Gender extends Component {
constructor(props) {
super(props);
}
nextScr = (gend) => {
alert(`gen: ${gend} \n this.props.gen: ${this.props.gen}`)
//***** here I am getting undefined ****
if(gend!= null) {
this.props.navigation.navigate('Info');
}
}
render() {
const { gen } = this.props;
return (
<View style={style.container}>
<View style={style.bcont}>
{/* this.storeData("Male") this.storeData("Female") */}
<Btn name="gender-male" txt="Male" click={() => this.props.saveMale('male')}
bstyl={(gen == 'Male') ? [style.btn, style.btnsel] : style.btn} />
<Text style={style.hi}>OR</Text>
<Btn name="gender-female" txt="Female" click={() => this.props.saveFemale('female')}
bstyl={(gen == 'Female') ? [style.btn, style.btnsel] : style.btn} />
</View>
<Text>Gender Value is: {this.props.gen}</Text>
// **** here not getting gen value ****
<Next name="chevron-right" nextClk={ () => this.nextScr(gen)} />
</View>
);
}
}
const mapStateToProps = state => {
const { gen } = state
return {
gen: gen,
};
};
const mapDispatchToProps = dispatch => {
return {
saveMale: (gen) => {
dispatch(saveMale(gen));
},
saveFemale: (gen) => {
dispatch(saveFemale(gen));
}
}
};
export default connect(mapStateToProps, mapDispatchToProps)(Gender);
These are my actions:
export const saveMale = (gen) => ({
type: MALE_SAVE,
payload: gen
});
export const saveFemale = (gen) => ({
type: FEMALE_SAVE,
payload: gen
});
Following is my reducer:
const initialState = {
gen: null
}
export function genSave(state=initialState, action) {
switch(action.type) {
case MALE_SAVE:
alert(`state in MALE_SAVE: ${action.payload}`);
return { ...state, gen: action.payload };
case FEMALE_SAVE:
alert(`state in FEMALE_SAVE: ${action.payload}`);
return { ...state, gen: action.payload };
default:
alert(`state in default gender save: ${JSON.stringify(state)}`);
return state;
};
}
I am getting action.payload alert values but in the component I am not getting values. How do I solve this problem ?? Thanks in advance.

Can you try like this?
...
nextScr(gend) {
alert(`gen: ${gend} \n this.props.gen: ${this.props.gen}`)
if(gend!= null) {
this.props.navigation.navigate('Info');
}
}
render() {
const { gen } = this.props;
return (
<View style={style.container}>
<View style={style.bcont}>
{/* this.storeData("Male") this.storeData("Female") */}
<Btn name="gender-male" txt="Male" click={() => this.props.saveMale('male')}
bstyl={(gen == 'Male') ? [style.btn, style.btnsel] : style.btn} />
<Text style={style.hi}>OR</Text>
<Btn name="gender-female" txt="Female" click={() => this.props.saveFemale('female')}
bstyl={(gen == 'Female') ? [style.btn, style.btnsel] : style.btn} />
</View>
<Text>Gender Value is: {this.props.gen}</Text>
<Next name="chevron-right" nextClk={ () => this.nextScr(this.props.gen)} />
</View>
);
}
...

I believe your mapStateToProps could be the problem depending on how you initialize your store. Right now it assumes gen is a property on the base store but it is likely you have a combineRecucers call when you create the store that adds another object layer.

Related

React Native & Redux : undefined is not an object (evaluating 'state.counter')

I was trying to use Redux in my React Native project to create a counter app. But then I encounter this error. It says sth like undefined is not an object (evaluating 'state.counter')
Please have a look at my code.
Counter.js
class Counter extends Component {
state = {counter: 0};
render() {
return (
<View style={styles.container}>
<View style={styles.counterPart}>
<TouchableOpacity onPress={() => this.props.increaseCounter()}>
<Text style={styles.text}>Increase</Text>
</TouchableOpacity>
<Text style={styles.text}>{this.props.counter}</Text>
<TouchableOpacity onPress={() => this.props.decreaseCounter()}>
<Text style={styles.text}>Decrease</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
function mapStateToProps(state) {
return {
counter: state.counter,
};
}
function mapDispatchToProps(dispatch) {
return {
increaseCounter: () => dispatch({type: 'INCREASE_COUNTER'}),
decreaseCounter: () => dispatch({type: 'DECREASE_COUNTER'}),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
The error seems to result from mapStateToProps(state) function above.
App.js
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'INCREASE_COUNTER':
console.log('Enter INCREASE_COUNTER reducer');
return {counter: state.counter + 1};
case 'DECREASE_COUNTER':
console.log('Enter DECREASE_COUNTER reducer');
return {counter: state.counter - 1};
}
return state;
};
const store = createStore(reducer);
const initialState = {
counter: 0,
};
class App extends Component {
render() {
return (
<Provider store={store}>
<Counter />
</Provider>
);
}
}
I would appreciate if you can provide a solution or a suggestion to this issue. Thank you.
I think the problem is that you can't access to initialState in reducer, try to move declaration on the top of reducer like so.
const initialState = {
counter: 0,
};
const reducer = (state = initialState, action) => {
switch (action.type) {
...
}
}
I think that your problem will be solved by adding default situation in switch.
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'INCREASE_COUNTER':
console.log('Enter INCREASE_COUNTER reducer');
return {counter: state.counter + 1};
case 'DECREASE_COUNTER':
console.log('Enter DECREASE_COUNTER reducer');
return {counter: state.counter - 1};
default: // <--- HERE
return state; // <--- HERE
}
};

How to add a condition for the user's connection?

I want to add to this code the condition: if the user is connected, he goes directly to BottomTabNavigator (which is the opening of the application) and otherwise he goes in the Authentication file which will allow him to either connect or register. How can I do this ?
Usually I used
import React from "react";
import { NavigationContainer } from "#react-navigation/native";
import BottomTabNavigator from "./Navigation/TabNavigator";
const App = () => {
return (
<NavigationContainer>
<BottomTabNavigator />
</NavigationContainer>
);
}
export default App
Usually in a class component I used this, but I don't know how to do with the new syntax:
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
isFirstConnection: true,
status: 0,
fontLoaded: false
};
}
async UNSAFE_componentWillMount() {
let lang = await retrieveAppLang();
let isConnected = await userSessionActive();
if (lang.length == 2) {
i18n.changeLanguage(lang);
}
if (isConnected === true && this.props && this.props.navigation) {
this.props.navigation.navigate("TabBar");
}
}
async componentDidMount() {
await Font.loadAsync({
FunctionLH: require("./assets/fonts/FunctionLH-Light.ttf")
});
const data = await this.performTimeConsumingTask();
if (data !== null) {
this.setState({
isFirstConnection: false,
status: 1,
fontLoaded: true,
});
}
}
performTimeConsumingTask = async () => {
return new Promise((resolve) =>
setTimeout(() => {
resolve("result");
}, 750)
);
};
render() {
if (this.state.status == 1) {
if (this.state.isFirstConnection && this.state.fontLoaded) {
return <SplashScreen />;
} else {
return <Navigation screenProps={'Authentication'} />;
}
}
return (
<ImageBackground
source={require("./assets/images/background.jpg")}
style={{ flex: 1 }}
>
<View style={[styles2.container, styles2.containerCentered]}>
<StatusBar hidden={true} />
<View style={styles2.subContainer}>
<Image
style={styles2.logo}
source={require("./assets/images/logo.png")}
/>
<ActivityIndicator size="large" color="#43300E" />
<Text>{i18n.t("app.loading") + "..."}</Text>
</View>
</View>
</ImageBackground>
);
}
}}
the 'isConnected' is on a file "myPreferences"
export async function userSessionActive() {
let userAuthorizationCode = await retrieveProfileAuthorizationCode();
let userUserId = await retrieveProfileUserId();
let userEmail = await retrieveProfileLogin();
let is_connected = false;
if (userAuthorizationCode != '' && userUserId !== null && parseInt(userUserId) > 0 && userEmail != '') {
is_connected = true;
}
return is_connected;
}
I thought doing something like this but it's not working :
function App(userSessionActive) {
const isConnected = userSessionActive.isConnected;
if (isConnected) {
return <NavigationContainer>
<BottomTabNavigator />
</NavigationContainer>;
}
return <StackNavigator screenProps={'Authentication'}/>;
}
export default App

How to redirect back and refresh the parent view

Hello everyone I am new to react-native.
React native V 0.61
React navigation V 5.x
I have a search bar for a weather API once it is clicked you arrive to the temperature list.
If you type an invalid city, I want to be redirected back with an error message. My problem is that I do not know how to do this because my parent component is not getting updated!
SEARCH.JS
export default class Search extends React.Component {
constructor(props){
super(props)
this.state = {
city: 'Liège',
error: ''
}
}
setCity(city){
this.setState({city})
}
submit(){
Keyboard.dismiss();
this.props.navigation.navigate('Result', {state: this.state});
}
render(){
if (this.state.error != '') {
var error = this.state.error;
} else {
var error = 'hello world';
}
return (
<View style={GlobalStyle.container}>
<TextInput
onChangeText={(text) => this.setCity(text)}
onSubmitEditing={() => this.submit()}
style={GlobalStyle.input}
value={this.state.city}
/>
<Button color={GlobalStyle.color} onPress={() => this.submit()} title="Lancé recherche"/>
<Text>{error}</Text>
</View>
);
}}
LIST.JS
export default class List extends React.Component {
constructor (props){
super(props)
this.state={
city: this.props.route.params.state.city,
result: null
}
this.fetchWeather();
}
fetchWeather(){
axios.get('http://api.openweathermap.org/data/2.5/forecast?q='+this.state.city+'&appid=MYKEY&units=metric&lang=fr')
.then((response) => {
this.props.route.params.state.error = '';
this.setState({result: response.data})
})
.catch(error => {
// console.log(error.response.data.message);
// console.log(this.props.navigation.setOptions());
// this.props.navigation.setParams.onReturn({city: this.props.route.params.state, error: error.response.data.message});
// this.props.state.error = 'errror';
this.props.route.params.state.error = error.response.data.message;
// console.log(this.props.route.params.state.error);
this.props.navigation.goBack();
// this.props.navigation.navigate('Search', {error: error.response.data.message});
});
}
render(){
if(this.state.result === null){
return (
<ActivityIndicator color={GlobalStyle.color} size="large"/>
);
}else{
return (
<FlatList
data={this.state.result.list}
renderItem={({item}) => <WeatherRow day={item}/>}
keyExtractor={item => item.dt.toString()}
/>
);
}
}}
Thank you for your help and have a good day.
In Search.js add
onBackToHome =() => {
this.setState({
city: 'Liège',
error: ''
})
}
submit(){
Keyboard.dismiss();
this.props.navigation.navigate('Result', {state: this.state, backToRefresh: this.onBackToHome});
}
Then in List.js
//Add this line just above this.props.navigation.goBack()
this.props.navigation.state.params.backToRefresh();

React native : Is there any way to redirect the initial route to another page

I'm new in react native and currently developing a react-native app that require login. After successful login, the view change into Homepage. The problem is after i close and re-open the app, it shows me LoginPage again.Is there any way to redirect the initial route to another page
class Main extends Component {
_renderScene(route, navigator) {
if (route.id === 1) {
return <LoginPage navigator={navigator} />
} else if (route.id === 2) {
return <HomePage navigator={navigator} />
} else if (route.id === 3) {
return <DetailPage navigator={navigator} />
} else if (route.id === 4) {
return <CreateBookingPage navigator={navigator} />
}
}
_configureScene(route) {
return Navigator.SceneConfigs.PushFromRight;
}
render() {
return (
<Navigator
initialRoute={{id: 1, }}
renderScene={this._renderScene}
configureScene={ () => { return Navigator.SceneConfigs.PushFromRight; }} />
);
}
}
/////after some changes I get into this but still its rendering the login is I done something wrong////////
componentWillMount() {
AsyncStorage.getItem('key').then( (value) => {
if(value==="yes") {
this.setState({ loader: false, logged: true})
} else {
this.setState({ loader: false })
}
})
}
render() {
const routeId = this.state.logged ? 2 : 1;
if(this.state.loader) {
return (
<View /> // loading screen
);
}
return (
<Navigator
initialRoute={{id: routeId }}
renderScene={this._renderScene}
configureScene={ () => { return Navigator.SceneConfigs.PushFromRight; }} />
);
}
On successful login you could set a token/value in the local storage of the device and on logout clear this value.
You could check this value for setting the initial route. Asyncstorage can be used in setting and removing the logged status.
EDIT:
Initial state of loader should be true, logged should be false
componentWillMount() {
AsyncStorage.getItem('key').then( (value) => {
if(value) {
this.setState({ loader: false, logged: true})
} else {
this.setState({ loader: false })
}
})
}
render() {
const { loader } = this.state;
const routeId = logged ? 2 : 1;
if(loader) {
return (
<View /> // loading screen
);
}
return (
<Navigator
initialRoute={{id: routeId }}
renderScene={this._renderScene}
configureScene={ () => { return Navigator.SceneConfigs.PushFromRight; }} />
);
}
Replace with your render function and try it.
render() {
var loggedStatus = true; // change here for login screen
var routeId = loggedStatus ? 2 : 1;
return (
<Navigator
initialRoute={{id: routeId }}
renderScene={this._renderScene}
configureScene={ () => { return Navigator.SceneConfigs.PushFromRight; }} />
);
}

How to re render sub component on prop change with redux?

I have a react native app using redux and immutable js. When i dispatch an action from my main screen, it goes through my actions, to my reducer and then back to my container, however, the view doesn't update and componentWillReceieveProps is never called. Furthermore, the main screen is a list whose items are sub components Item. Here's the relevant code for the issue, if you want to see more let me know.
Render the row with the data:
renderRow(rowData) {
return (
<Item item={ rowData } likePostEvent={this.props.likePostEvent} user={ this.props.user } removable={ this.props.connected } />
)
}
The part of Item.js which dispatches an action, and shows the result:
<View style={{flex: 1, justifyContent:'center', alignItems: 'center'}}>
<TouchableOpacity onPress={ this.changeStatus.bind(this, "up") }>
<Image source={require('../img/up-arrow.png')} style={s.upDownArrow} />
</TouchableOpacity>
<Text style={[s.cardText,{fontSize:16,padding:2}]}>
{ this.props.item.starCount }
</Text>
<TouchableOpacity onPress={ this.changeStatus.bind(this, "down") }>
<Image source={require('../img/up-arrow.png')} style={[s.upDownArrow,{transform: [{rotate: '180deg'}]}]} />
</TouchableOpacity>
</View>
The action dispatched goes to firebase, which has an onChange handler that dispatches another action.
The reducer:
const initialState = Map({
onlineList: [],
offlineList: [],
filteredItems: [],
connectionChecked: false,
user: ''
})
...
...
case ITEM_CHANGED:
list = state.get('onlineList')
if(state.get('onlineList').filter((e) => e.id == action.item.id).length > 0){
let index = state.get('onlineList').findIndex(item => item.id === action.item.id);
list[index] = action.item
list = list.sort((a, b) => b.time_posted - a.time_posted)
}
return state.set('onlineList', list)
.set('offlineList', list)
The container:
function mapStateToProps(state) {
return {
onlineItems: state.items.get('onlineList'),
offlineItems: state.items.get('offlineList'),
filteredItems: state.items.get('filteredItems'),
connectionChecked: state.items.get('connectionChecked'),
connected: state.items.get('connected'),
user: state.login.user
}
}
Where I connect the onChange:
export function getInitialState(closure_list) {
itemsRef.on('child_removed', (snapshot) => {
closure_list.removeItem(snapshot.val().id)
})
itemsRef.on('child_added', (snapshot) => {
closure_list.addItem(snapshot.val())
})
itemsRef.on('child_changed', (snapshot) => {
closure_list.itemChanged(snapshot.val())
})
connectedRef.on('value', snap => {
if (snap.val() === true) {
closure_list.goOnline()
} else {
closure_list.goOffline()
}
})
return {
type: GET_INITIAL_STATE,
connected: true
}
}
Calling get initial state:
this.props.getInitialState({
addItem: this.props.addItem,
removeItem: this.props.removeItem,
goOnline: this.props.goOnline,
goOffline: this.props.goOffline,
itemChanged: this.props.itemChanged
})
Any suggestions are welcome, thanks so much!
The source of your issue could be with the call to Firebase. If it is an asynchronous call, it's return callback might not be returning something that can be consumed by your action.
Do you know if it is returning a Promise? If that is the case, middleware exists that handle such calls and stops the calling of an action until a correct response is received. One such middleware is Redux-Promise.
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore,combineReducers } from 'redux' //Redux.createStore
import { Provider,connect } from 'react-redux';
//Функція яка змінює store
const hello = (state= {message:'none'}, action) => {
switch (action.type) {
case 'HELLO':
return Object.assign({}, state, {message:"hello world"});
break
case 'buy':
return Object.assign({}, state, {message:"buy"});
break;
case 'DELETE':
return Object.assign({}, state, {message:"none"});
break;
default :
return state;
}
};
const price = (state= {value:0}, action) => {
switch (action.type) {
case 'HELLO':
return Object.assign({}, state, {value: state.value + 1 });
break;
default :
return Object.assign({}, state, {value:0});
}
};
const myApp = combineReducers({
hello,price
});
//створюємо store
let store = createStore(myApp);
let unsubscribe = store.subscribe(() => console.log(store.getState()))
//VIEW
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<p>value: {this.props.price}</p>
<a href="#" onClick={this.props.onClick}>click</a><b>{this.props.message}</b>
</div>
)
}
}
//mapStateToProps() для чтения состояния и mapDispatchToProps() для передачи события
const mapStateToProps = (state, ownProps) => {
return {
message: state.hello.message,
price: state.price.value
}
};
const mapDispatchToProps = (dispatch, ownProps) => {
return {
onClick: () => {
var items= ['HELLO','buy','DELETE','error']
var item = items[Math.floor(Math.random()*items.length)];
dispatch({ type: item })
}
}
}
const ConnectedApp = connect(
mapStateToProps,
mapDispatchToProps
)(App);
ReactDOM.render(
<Provider store={store}>
<ConnectedApp />
</Provider>,
document.getElementById('app')
);