Get value from Picker onValueChange in react-native - android

I am developing an app using react native.
Already I have implemented a list of user data. One example of data is this.
UID234 {"name":"Chris","age":31,"traits":{"eyes":"blue","shoe_size":10}}
Here's my code.
import RNPickerSelect from 'react-native-picker-select';
...
<RNPickerSelect
onValueChange={(value) => console.log(value)} // そのユーザーのvalueを取る
// {console.log(to_user)}
items={[
{ label: 'Chris', value: 'UID234' },
{ label: 'Marge', value: 'UID345' },
{ label: 'Yuri', value: 'UID456' }
]}
style={pickerSelectStyles}
placeholder={{ label: '選択してください', value: '' }}
/>
In this case, when I select Chris, I can see "UID234" is shown on the console.
What I want to do is to print "Chris" on the screen.
How can I do that? Please help me...

The react-native-picker-select docs say the index can be passed in via the onValueChange callback. Use that to get the label.
import RNPickerSelect from 'react-native-picker-select';
...
const itemList = [
{ label: 'Chris', value: 'UID234' },
{ label: 'Marge', value: 'UID345' },
{ label: 'Yuri', value: 'UID456' }
];
<RNPickerSelect
onValueChange={(value, index) => console.log(itemList[index].label)} // <- get label from array
// {console.log(to_user)}
items={itemList}
style={pickerSelectStyles}
placeholder={{ label: '選択してください', value: '' }}
/>

you'll need to save the data in state form in order to show the label
this.state ={
user : ''
}
<RNPickerSelect
onValueChange={(value) => {this.setState({user: value,})}}
items={[
{ label: 'Chris', value: 'UID234' },
{ label: 'Marge', value: 'UID345' },
{ label: 'Yuri', value: 'UID456' }
]}
style={pickerSelectStyles}
value={this.state.user}
placeholder={{ label: '選択してください', value: '' }}
/>

Related

Passing useState update function to child causes undefined function error

Using expo and react-native I have created a screen called MainStateScreen this screen essentially has two direct children called OptionsComponent and NavigationButton. OptionsComponent has multiple children that when modified, update the state of the MainStateScreen so we can pass that as a prop to the next screen with the NavigationButton. That's how it's supposed to work at least.
Instead, when I try to modify the OptionsComponent's children and use MainStateScreen's update function I get the following error.
Here is pseudo-code of my component composition:
MainStateScreen.js
const defaultOptionsObject = [
{
name: '',
value: 1,
count: 0,
},
{
name: '',
value: 8,
count: 0,
},
{
name: '',
value: 20,
count: 0,
},
{
name: '',
value: 25,
count: 0,
},
{
name: '',
value: 30,
count: 0,
},
{
name: '',
value: 32,
count: 0,
},
{
name: '',
value: 100,
count: 0,
},
]
const MainStateScreen = () => {
const [options, setOptions] = useState(defaultOptionsObject)
return (
<View>
<ScrollView>
<OptionsComponent options={options} setOptions={setOptions} />
</ScrollView>
<NavigationButton onPress={() => navigation.push('OtherScreen', { options })} />
</View>
)
}
OptionsComponent.js
const SingleOptionComponent = ({ index, option, options, setOptions }) => {
const [stateOption, setStateOption] = useState({ ...option })
const updateCount = val => {
const tempOption = { ...stateOption }
if (val) {
tempOption.count += 1
} else if (tempOption.count !== 0) {
tempOption.count -= 1
}
setStateOption(tempOption)
}
useEffect(() => {
const tempOptions = [ ...options ]
tempOptions[index] = { ...stateOption }
setOptions(tempOptions) // Commenting this out removes the error.
}, [stateOption])
const AddSubtractButton = () => {
return (
<View>
<TouchableHighlight onPress={() => updateCount(true)}>
<Text>Add</Text>
</TouchableHighlight>
<TouchableHighlight onPress={() => updateCount(false)}>
<Text>Subtract</Text>
</TouchableHighlight>
</View>
)
}
return (
<ListItem rightElement={AddSubtractButton} />
)
}
const OptionsComponent = ({ options, setOptions }) => {
return (
<View>
{options.map((option, index) => {
return (
<SingleOptionComponent
key={`${option?.value}-${index}`}
index={index}
option={option}
options={options}
setOptions={setOptions}
/>
)
})}
<View/>
)
}
The exact error is:
TypeError: undefined is not a function (near '...options.map...')
This error is located at:
in OptionsComponent (at MainStateScreen.js)
BUT when I console.log the setOptions function in the useEffect of SingleOptionComponent, Function setOptions is printed in the console.
If I remove the setOptions(tempOptions) call from the useEffect inside the SingleOptionComponent the error goes away but I need something like that to accomplish the pass up to the parent and over to the NavigationButton.
What could I be doing wrong?
A couple things I would suggest that may solve the issue without re-creating it on my machine. Try adding setOptions to the useEffect dependency array. Maybe for some reason it isn't getting an updated setOptions as it is passed down to the grandchild component. You also should pass in options to it as your effect does dependent on when that value changes.
If that doesn't work try using spread operator setOptions({ ... tempOptions}).
If that doesn't work I would suggest handling the setOptions inside the uppermost component and instead passing a callback down to the grandchild rather than the setOptions function. That callback and get the option pressed in the parent and then update it appropriately based off option._id (I would supply a unique _id to each option rather than dynamically calculating it just to be able to do the above.). Also makes code easier to understand what is going on as the parent will handle it's own state that it needs and setting it appropriately.

Alert.alert not working in React native iOS, but perfectly fine in Android

Please check the code ,
import {
Alert,
} from 'react-native';
checkForSendingOtp = () => {
let hash = 'aBcDeGgd';
Platform.OS === 'android'
? RNOtpVerify.getHash()
.then(text => {
hash = text + ''.replace('[', '');
hash = hash + ''.replace(']', '');
})
.then(() => {
this.sendDataForOtp(hash);
})
.catch(console.log)
: this.sendDataForOtp(hash);
};
sendDataForOtp(hash) {
axios.post(url,{hash:hash}).then(response=>{
Alert.alert(
'Login Failed',
'Multiple Logins Are Found. \n Logout From All Other Devices to Continue.',
[
{
text: 'Proceed ?',
onPress: () => {}
},
{
text: 'No',
onPress: () => {},
},
],
{cancelable: false},
);
});
}
render() {
return (
<Ripple
style={{
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
right: 0,
}}
onPress={this.checkForSendingOtp}
/>
)}
This snippet will work fine in android but not showing in iOS. why ?
Nb :- this is the most of the code that I can share right now , Edited the code please check it now and let me know if you have any questions.
I don't exactly know what happened, There were also a model component which I used for showing custom loading, after removing the model component the alerts starts working.
Replace alert code with below
Alert.alert(
'Login Failed',
'Multiple Logins Are Found. \n Logout From All Other Devices to Continue.',
[
{
text: 'Proceed ?',
onPress: () => {}
},
{
text: 'No',
onPress: () => {},
style: 'cancel'
}
],
{ cancelable: false }
);
It's probably related to this issue: https://github.com/facebook/react-native/issues/10471
For some weird reasons, having the Alert called in a "setTimeout" function make it work. But it’s clearly NOT the best way to avoid this problem. You can also try this solution: https://github.com/facebook/react-native/issues/10471#issuecomment-513641325
setTimeout(() => {
Alert.alert(
"title",
"content",
[
{
text: "ok",
onPress: () => {
},
},
],
)
}, 10)
setTimeout(() => {
//TODO use return and setTimeout
return Alert.alert(
i18n.t('notAtYard.alertTitle'),
'Invalid username or password',
[
{
text: 'Ok',
onPress: () => {
setUserPassword('');
setUsername('');
},
}
],
{cancelable: false},
);
}, 100);
So in my case, I was not using any custom Modal because for custom Modal the solution is simple to wait until the Modal is closed but, I was using the react-native Spinner component, and I was hiding it properly using the visible={loading} prop and yes, the issue was also not resolved after setting cancelable: false in the Alert.alert config and when I increased the timeout to 5000 it worked even without defining cancelable: false in the Alert.alert config but this a lot of timeout was not good for UX so I quickly checked out the Spinner component props and I came to know that it does have cancelable?: boolean | undefined prop so now when I used Spinner component like below it worked even without timeout.
<Spinner
visible={loading}
cancelable={true}
/>
TLDR: use cancelable={true} prop with Spinner component.

React Native FlatList Grid custom view

I am developing a react native app that shows data in the flatlist grid view.
for that, I followed the code which I found on the expo. I work fine. But what I need is, I want the first row should render one item only. so that I can use the empty space to show some data first.
here is the Expo link.
https://snack.expo.io/#savadks1818/react-native-flatlist-grid
and here is the code
import React from 'react';
import { StyleSheet, Text, View, FlatList, Dimensions } from 'react-native';
const data = [
{ key: 'A' }, { key: 'B' }, { key: 'C' }, { key: 'D' }, { key: 'E' }, { key: 'F' }, { key: 'G' }, { key: 'H' }, { key: 'I' }, { key: 'J' },
{ key: 'K' },
// { key: 'L' },
];
const formatData = (data, numColumns) => {
const numberOfFullRows = Math.floor(data.length / numColumns);
let numberOfElementsLastRow = data.length - (numberOfFullRows * numColumns);
while (numberOfElementsLastRow !== numColumns && numberOfElementsLastRow !== 0) {
data.push({ key: `blank-${numberOfElementsLastRow}`, empty: true });
numberOfElementsLastRow++;
}
return data;
};
const numColumns = 3;
export default class App extends React.Component {
renderItem = ({ item, index }) => {
if (item.empty === true) {
return <View style={[styles.item, styles.itemInvisible]} />;
}
return (
<View
style={styles.item}
>
<Text style={styles.itemText}>{item.key}</Text>
</View>
);
};
render() {
return (
<FlatList
data={formatData(data, numColumns)}
style={styles.container}
renderItem={this.renderItem}
numColumns={3}
/>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginVertical: 20,
},
item: {
backgroundColor: '#4D243D',
alignItems: 'center',
justifyContent: 'center',
flex: 1,
margin: 3,
height: Dimensions.get('window').width / numColumns, // approximate a square
},
itemInvisible: {
backgroundColor: 'transparent',
},
itemText: {
color: '#fff',
},
});
you can do this by adding new obects in the data array in desired position
const data = [
{ key: 'A' },
{ empty: true, key: 'xxx' },
{ empty: true, key: 'xxx' },
{ key: 'B' },
{ key: 'C' },
{ key: 'D' },
{ key: 'E' },
{ key: 'F' },
{ key: 'G' },
{ key: 'H' },
{ key: 'I' },
{ key: 'J' },
{ key: 'K' },
{ key: 'L' },
];
to add item do
data.splice(1, 0, { empty: true, key: 'xxx' });

React-Native alternative to AlertIOS.prompt for android?

I am following a tutorial for react-native, however they are doing it for IOS, there is one part where they use AlertIOS.prompt like this
AlertIOS.prompt(
'Add New Item',
null,
[
{text: 'Cancel', onPress: () => console.log('Cancel Pressed'), style: 'cancel'},
{
text: 'Add',
onPress: (text) => {
this.itemsRef.push({ title: text })
}
},
],
'plain-text'
);
I am trying to remake this for android but cannot get it working, I did find this https://www.npmjs.com/package/react-native-prompt
import Prompt from 'react-native-prompt';
<Prompt
title="Say something"
placeholder="Start typing"
defaultValue="Hello"
visible={ this.state.promptVisible }
onCancel={ () => this.setState({
promptVisible: false,
message: "You cancelled"
}) }
onSubmit={ (value) => this.setState({
promptVisible: false,
message: `You said "${value}"`
}) }/>
However I cannot get this to work either, It is supposed to display the prompt when I press a button but nothing happens..
Here is the full original code with AlertIOS
'use strict';
import React, {Component} from 'react';
import ReactNative from 'react-native';
const firebase = require('firebase');
const StatusBar = require('./components/StatusBar');
const ActionButton = require('./components/ActionButton');
const ListItem = require('./components/ListItem');
const styles = require('./styles.js')
const {
AppRegistry,
ListView,
StyleSheet,
Text,
View,
TouchableHighlight,
AlertIOS,
} = ReactNative;
// Initialize Firebase
const firebaseConfig = {
apiKey: "AIzaSyA9y6Kv10CAl-QOnSkMehOyCUejwvKZ91E",
authDomain: "dontforget.firebaseapp.com",
databaseURL: "https://dontforget-bd066.firebaseio.com",
storageBucket: "dontforget-bd066.appspot.com",
};
const firebaseApp = firebase.initializeApp(firebaseConfig);
class dontforget extends Component {
constructor(props) {
super(props);
this.state = {
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
})
};
this.itemsRef = this.getRef().child('items');
}
getRef() {
return firebaseApp.database().ref();
}
listenForItems(itemsRef) {
itemsRef.on('value', (snap) => {
// get children as an array
var items = [];
snap.forEach((child) => {
items.push({
title: child.val().title,
_key: child.key
});
});
this.setState({
dataSource: this.state.dataSource.cloneWithRows(items)
});
});
}
componentDidMount() {
this.listenForItems(this.itemsRef);
}
render() {
return (
<View style={styles.container}>
<StatusBar title="Grocery List" />
<ListView
dataSource={this.state.dataSource}
renderRow={this._renderItem.bind(this)}
enableEmptySections={true}
style={styles.listview}/>
<ActionButton onPress={this._addItem.bind(this)} title="Add" />
</View>
)
}
_addItem() {
AlertIOS.prompt(
'Add New Item',
null,
[
{text: 'Cancel', onPress: () => console.log('Cancel Pressed'), style: 'cancel'},
{
text: 'Add',
onPress: (text) => {
this.itemsRef.push({ title: text })
}
},
],
'plain-text'
);
}
_renderItem(item) {
const onPress = () => {
AlertIOS.alert(
'Complete',
null,
[
{text: 'Complete', onPress: (text) => this.itemsRef.child(item._key).remove()},
{text: 'Cancel', onPress: (text) => console.log('Cancelled')}
]
);
};
return (
<ListItem item={item} onPress={onPress} />
);
}
}
AppRegistry.registerComponent('dontforget', () => dontforget);
Could anyone tell me how I could make this work for android?
I think you can use the following libraries : https://github.com/mmazzarolo/react-native-dialog
Example code to get user input are as follows (not in docs)
<Dialog.Container visible={true}>
<Dialog.Title>Account delete</Dialog.Title>
<Dialog.Description>
Do you want to delete this account? You cannot undo this action.
</Dialog.Description>
<Dialog.Input label="Cancel" onChangeText={(text) => this.setState({username : text})} />
<Dialog.Button label="Delete" onPress={() => {
console.log(this.state.username);
this.setState({promptUser : false});
}} />
</Dialog.Container>
These looks like a good alternative since its natively implemented
https://github.com/shimohq/react-native-prompt-android
Actually, you can use a cross-platform prompt component that works fine both on Android and Ios. The link is given below.
https://www.npmjs.com/package/react-native-prompt-crossplatform and its
repository
link is
https://github.com/Ramiah-Viknesh/react-native-prompt-crossplatform
I developed a small utility to solve this issue. It might be useful.
https://github.com/eleviven/react-native-alertbox

remove title bar or add button to it in sencha

This is screenshot of my sencha app deployed on android. I view two blue bars on the top. what i want is just to remove one of them. Any idea how to do it?
The codeis given below. hope this will help
Ext.define('appointMeDr.view.signUp.SignUp',{
extend:'Ext.form.Panel',
xtype:'signUpXType',
config:{
scrollable:'vertical',
items:[
{
xtype: 'toolbar',
title: 'Sign Up',
docked: 'top',
items:[ {
xtype:'button',
text: 'Back',
ui: 'back',
cls: 'back',
name: 'backToLogin'
}
]
},
{
xtype:'fieldset',
defaults :{
labelWidth : '120px'
},
items:[
{
xtype:'textfield',
label:'<sup>*</sup> Full Name: ',
placeHolder:'Full Name',
name:'name'
},
{
xtype: 'emailfield',
label: '<sup>*</sup> Email',
placeHolder:'Email',
name: 'email'
},
{
xtype:'textfield',
label:'<sup>*</sup> User Name: ',
placeHolder:'User Name',
name:'username'
},
{
xtype: 'passwordfield',
label: '<sup>*</sup> Password',
placeHolder:'Password',
name: 'password'
},
{
xtype:'textfield',
label:'<sup>*</sup> Age: ',
placeHolder:'Age',
name:'age'
},
{
xtype: 'selectfield',
name:'gender',
label: 'Gender',
options: [
{
text: 'Male',
value: 'Male'
},
{
text: 'Female',
value: 'Female'
}
]
},
{
xtype:'textfield',
label:'<sup>*</sup> Address : ',
placeHolder:'Address',
name:'address'
},
{
xtype: 'selectfield',
name:'Domain',
label: 'Select Domain',
options: [
{
text: 'Patient',
value: 'first'
},
{
text: 'Doctor',
value: 'second'
},
{
text: 'Guardian',
value: 'third'
},
{
text: 'Attendant',
value: 'forth'
}
]
}
]
},{
xtype:'panel',
margin:'10px',
items:[
{
xtype:'button',
text:'Sign Up',
flex:1,
name:'userSignUpBtn',
docked:'right'
}
]
}
]
}
});
youre probably using a navigation view and loading another panel which contains a toolbar into the navigation view so what you get is
1. blue bar from the navigation view
2. 2nd blue bar from the panel view
What you could do is load the view directly into the viewport instead of the navigation view
hope this helps :)

Categories

Resources