Conditional Rendering on Items of Native Base Picker [React Native] - react-native

I’m using ‘Native Base’ components for our product and going good with this,
but I’m stuck at one point and it is around putting Items in Nativebase Picker. My code is like this
Render Method code -
render(){
return (
<View style={{marginTop: 20, flexDirection:'row', flexWrap:'wrap', justifyContent:'space-around', alignItems:'center'}}>
<View style={{flex:1, justifyContent:'center', alignItems:'flex-end' }}>
<Button
style={{ backgroundColor: '#6FAF98', }}
onPress={this._showDateTimePicker}
>
<Text>Select Date</Text>
</Button>
</View>
<View style={{flex:1, justifyContent:'center', alignItems:'stretch'}}>
<Picker
style={{borderWidth: 1, borderColor: '#2ac', alignSelf:'stretch'}}
supportedOrientations={['portrait','landscape']}
iosHeader="Select one"
mode="dropdown"
selectedValue={this.state.leaveType}
onValueChange={(value)=>this.setState({leaveType:value,})
//this.onValueChange.bind(this)
}>
<Item label="Full Day" value="leave1" />
{
this.showStartDateFirstHalf() // Here I want to show this picker item on the basis of a condition
}
<Item label="2nd half" value="leave3" />
</Picker>
</View>
<DateTimePicker
isVisible={this.state.isStartDatePickerPickerVisible}
onConfirm={this._handleDatePicked}
onCancel={this._hideDateTimePicker}
mode='date'
/>
</View>
);
}
showStartDateFirstHalf()
{
if(!this.state.isMultipleDays)
{
return(
<Item label="1st Half" value="leave2" />
);
}
}
So, this code is working fine if this.state.isMultipleDays is false, But when this.state.isMultipleDays is true, it means when it is in else part then i'm getting this error -
Cannot read property 'props' of undefined

I think there's an easier answer to this. Instead of creating the separate showStartDateFirstHalf() function try this:
render() {
const pickerItems = [
{
label: 'Full Day',
value: 'leave1',
},
{
label: '1st Half',
value: 'leave2',
},
{
label: '2nd Half',
value: 'leave3',
},
];
const filteredItems = pickerItems.filter(item => {
if (item.value === 'leave2' && this.state.isMultipleDays) {
return false;
}
return true;
});
// The 'return' statement of your render function
return (
...
<Picker ...>
{(() =>
filteredItems.map(item =>
<Item label={item.label} value={item.value} />
)()}
</Picker>
...
);
}
That way, you already have a list of items that is determined before the return statement of the render cycle. Also the use of filter instead of map will not just give you null as the second item if the condition is not met, but will remove the item altogether.

Related

How work with one array and two react native pickers

I want to use one data array for two of pickers. If one picker selected one option then I need to remove that option from other picker. I tried this from following code. I used array of length 2.
constructor(props) {
super(props);
this.state = {
startTown: 'Start Point',
endTown: 'End Point',
townList: null,
};
}
componentDidMount(){
firebase.database().ref('busRoutes').once("value",(snapshot)=>{
this.setState({townList:snapshot.val()})
})
}
<Box dir="row">
<Picker
style={{flex: 1}}
selectedValue={startTown}
onValueChange={(value) => {
this.setState({
startTown: value,
});
}}>
<Picker.Item value='Start Point' label='Start Point' />
{townList && townList.map((element,index)=>{
if(element===endTown) return;
return(<Picker.Item value={element} label={element} />)
})}
</Picker>
<Picker
style={{flex: 1}}
selectedValue={endTown}
onValueChange={(value) => {
this.setState({
endTown: value,
});
}}>
<Picker.Item value='End Point' label='End Point' />
{townList && townList.map((element,index)=>{
if(element===startTown) return;
return(<Picker.Item value={element} label={element} />)
})}
</Picker>
</Box>
But it gives error like this
length=2;index=2
here is the link to image with error.
https://imgur.com/ak3tvTB
I can`t put it here as my reputations are not enough

Is there a way to toggle a selected item on the renderItem SelectList in react-native?

I would like to toggle only one switch in the render item I am creating. So far when I toggle, all buttons toggle and I understand it's because I'm passing the same state to the value of all my switches but I don't know how to target just one
I have tried using other native components but none seem to be able to work like an input where I can pass an event and target it
state = {
containers: dataYay,
selectedContainers: [],
checked: false
}
containerSelected(value, item, index) {
console.log(item.Nbr, item.CtrId)
this.setState({ checked: value })
}
renderItem={({ item, index, section }) => {
return (
<Block padding={[10, 15, 20, 15]} color='#EFF8FF' margin={[0, 0, 1, 0]} row center middle key={index}>
<Block styles={styles.ContainerAssign} flex={0.3}>
<Switch
style={{ transform: [{ scaleX: .8 }, { scaleY: .8 }] }}
value={this.state.checked} // i would like to check just one of these
testID={index}
onValueChange={(value) => this.containerSelected(value, item, index)}
/>
</Block>
<Block styles={styles.ContainerInfo} padding={[0, 5, 0, 0]}>
<Text style={styles.ContainerInfoText}>
<Text style={styles.ContainerInfoTitle}> Container ID: </Text> {item.CtrId}
</Text>
... // code would be too long to displa
</Block>
<Button
... // code would be too long to display
</Button>
</Block>
)
}}
What I need help with is to find a way to pass the switch toggle to the current item on click?
Solution
Make your item to state component through React.Component class.
For example,
class Item extend React.Component {
state = {
checked: false,
}
render() {
return() {
<Block padding={[10, 15, 20, 15]} color='#EFF8FF' margin={[0, 0, 1, 0]} row center middle key={index}>
<Block styles={styles.ContainerAssign} flex={0.3}>
<Switch
style={{ transform: [{ scaleX: .8 }, { scaleY: .8 }] }}
value={this.state.checked} // i would like to check just one of these
testID={index}
onValueChange={(value) => this.containerSelected(value, item, index)}
/>
</Block>
<Block styles={styles.ContainerInfo} padding={[0, 5, 0, 0]}>
<Text style={styles.ContainerInfoText}>
<Text style={styles.ContainerInfoTitle}> Container ID: </Text> {item.CtrId}
</Text>
... // code would be too long to displa
</Block>
<Button
... // code would be too long to display
</Button>
</Block>
}
}
}
And use this item in your renderItem of parent.
renderItem = {({item}) => <Item item={item}/>}
Why?
this.state.checked was applied to renderItem's component. Thus, every renderItem component was using the same state called from this.state.checked. That is why it affects to all items. And I resolve this to use independent states in Item component.

How to call a method of a component

I am using native-base datepicker and want to call the ShowDatePicker method from outside the component. It owuld be something like this except:
This.DatePicker doesnt exist
I dont know if that method is exposed, or how to reach it..
i think it has something to do with using refs?
Thank you!
<Content>
<Button onPress={this.DatePicker.showDatePicker}>
<DatePicker props}/>
</Content>
DatePicker source code: https://github.com/GeekyAnts/NativeBase/blob/master/src/basic/DatePicker.js
Well, if you have to ref it just like the another answer says, as follows
<Content>
<Button onPress={this.DatePicker.showDatePicker}>
<DatePicker ref={ref => this.DatePicker = ref } {...this.props}/>
</Content>
However this will not fix your issue unless DatePicker component takes a props as ref. In short, even if you do that in your component, you will not have access to the showDatePicker.
Rather trying to do so, you can do this in two way (assuming you are trying to showhide component on button click.
Option 1:
Use a prop showDatePicker which will show hide the component.
For ex,
<Content>
<Button onPress={this.setState({showHide: !this.state.showHide})}>
<DatePicker showDatePicker={this.state.showHide} {...this.props} />
</Content>
then in DatePicker use this prop to do some logic.
Or Option 2,
Use conditional operator to show hide the whole component. w
For ex,
<Content>
<Button onPress={this.setState({showHide: !this.state.showHide})}>
{this.state.showHide && <DatePicker {...this.props} />}
</Content>
Let me know if you wanted to do something else, I will update the answer.
EDIT:
Looking at your code in gist.github.com/fotoflo/13b9dcf2a078ff49abaf7dccd040e179, I figured what you are trying to do.
In short, you trying to show datepicker on click of a button. Unfortunately, this is not possible at the moment looking at Nativebase - how to show datepicker when clicking input? and the documentation https://docs.nativebase.io/Components.html#date-picker-def-headref.
If you really wanna have it, you should think about these possible solution,
Option 1: fork native-base do your manipulation and use the datepicker or even submit the PR to native-base for future use.
Option2: you can use any 3rd party library for eg: https://www.npmjs.com/package/react-native-modal-datetime-picker.
Or my favourite option 3:
import { TouchableOpacity, Text, Modal, View, Platform, DatePickerIOS, DatePickerAndroid } from 'react-native';
state = {
currentDate: date,
showiOSDatePicker: false,
chosenDate: date,
formattedDate
}
showDatePicker = async () => {
if (Platform.OS === 'ios') {
this.setState({
showiOSDatePicker: true
});
} else {
const { chosenDate, currentDate } = this.state;
try {
const {action, year, month, day} = await DatePickerAndroid.open({
date: chosenDate,
maxDate: currentDate
});
if (action !== DatePickerAndroid.dismissedAction) {
const dateSelected = new Date(year, month, day);
const formattedDate = this.getFormattedDate(dateSelected)
this.setState({chosenDate: dateSelected, formattedDate});
console.log(formattedDate)
}
} catch ({code, message}) {
console.warn('Cannot open date picker', message);
}
}
}
render() {
const { showiOSDatePicker } = this.state;
return (
<View>
{showiOSDatePicker &&
<Modal
animationType="fade"
transparent
visible={showiOSDatePicker}
onRequestClose={() => {
Alert.alert('Modal has been closed.');
}}>
<View
style={{
display: 'flex',
flex: 1,
justifyContent: 'center'
}}
>
<View style={{
margin: 22,
backgroundColor: 'rgba(240,240,240,1)'
}}>
<View
style={{
borderBottomColor: 'rgba(87,191,229,1)',
borderBottomWidth: 2,
display: 'flex',
justifyContent: 'center',
height: 70,
paddingRight: 20
}}
>
<Text style={{
color: 'rgba(40,176,226,1)',
fontSize: 20,
paddingLeft: 20
}}>
{formattedDate}
</Text>
</View>
<DatePickerIOS
date={chosenDate}
onDateChange={this.setDate}
maximumDate={currentDate}
mode="date"
/>
<TouchableOpacity
style={{
borderTopColor: 'rgba(220,220,220,1)',
borderTopWidth: 1,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: 50
}}
onPress={this.onCloseDatePicker}
>
<Text>
Done
</Text>
</TouchableOpacity>
</View>
</View>
</Modal>
}
<TouchableOpacity onPress={this.showDatePicker}>
<Text>Show Date</Text>
</TouchableOpacity>
</View>
);
}
Let me know if this make sense, or I will put together a working example in https://snack.expo.io/
Cheers
you have to give a ref to DatePicker
<Content>
<Button onPress={this.DatePicker.showDatePicker}>
<DatePicker ref={ref => this.DatePicker = ref } props}/>
</Content>
I had the same problem and this was my solution:
NativeBase DatePicker:
<DatePicker
defaultDate={new Date(2018, 4, 4)}
minimumDate={new Date(2018, 1, 1)}
maximumDate={new Date(2020, 12, 31)}
locale={"es"}
timeZoneOffsetInMinutes={undefined}
modalTransparent={true}
animationType={"fade"}
androidMode={"default"}
placeHolderText="Select date"
textStyle={{ color: "green" }}
placeHolderTextStyle={{ color: "#d3d3d3" }}
onDateChange={this.setDate.bind(this)}
disabled={false}
ref={c => this._datePicker = (c) }
/>
And with this you can open the datePicker:
<Button onPress={()=>{ this._datePicker.setState({modalVisible:true})}}>
<Text>
showDatePicker
</Text>
</Button>
I hope it helps

How to close swipe item in react-native?

I have this code (get from native-base example) that is working fine. But after click on either sides (right or left), the 'swipe' still open. I know that there is a method called closeRow(), but I don't know how to apply in this case.
Left button is for 'split' item into 2 or more, while right button is to delete current item. What I need is to close all opened rows in this list. Why all? Because in the case of 'delete' function, the current item is deleted in the right way, but the next-one get right button opened (since it's the same 'index' of list, even if the item itself is different).
This is my current code:
<Container style={styles.container}>
<Header>
<Left>
<Button transparent onPress={() => this.props.navigation.goBack()}>
<Icon name="arrow-back" />
</Button>
</Left>
<Body>
<Title>{translate("title", { ...i18n_opt, name })}</Title>
</Body>
</Header>
<Content>
<Modal
isVisible={this.state.visibleModal === true}
animationIn={"slideInLeft"}
animationOut={"slideOutRight"}
>
{this._renderModalContent()}
</Modal>
<View style={styles.line}>
<View style={{ flex: 2 }}>
<Text style={[styles.alignCen, styles.headerTitle]}>
{translate("lap", { ...i18n_opt })}
</Text>
</View>
<View style={{ flex: 10 }}>
<Text style={[styles.alignCen, styles.headerTitle]}>
{translate("time", { ...i18n_opt })}
</Text>
</View>
<View style={{ flex: 3 }}>
<Text
style={[
{ paddingRight: 10 },
styles.alignRig,
styles.headerTitle
]}
>
{translate("diff", { ...i18n_opt })}
</Text>
</View>
</View>
<List
enableEmptySections
dataSource={laps2}
ref={c => {
this.component = c;
}}
renderRow={data => <PersonalRankItem dados={data} />}
renderLeftHiddenRow={data => (
<Button
full
onPress={() => {
this.setState({
...this.state,
visibleModal: true,
cur_tx_id: tx_id,
cur_lap: data.lap
});
}}
style={{
backgroundColor: "#CCC",
flex: 1,
alignItems: "center",
justifyContent: "center",
marginBottom: 6
}}
>
<MaterialCommunityIcons
name="arrow-split-vertical"
size={20}
color="#5e69d9"
/>
</Button>
)}
renderRightHiddenRow={(data, secId, rowId, rowMap) => (
<Button
full
danger
onPress={_ => {
//alert("Delete");
//console.log("Data.lap:",data.lap);
dispatchDeleteLap(tx_id, data.lap, true);
}}
style={{
flex: 1,
alignItems: "center",
justifyContent: "center",
marginBottom: 6
}}
>
<Icon active name="trash" size={20} />
</Button>
)}
leftOpenValue={70}
rightOpenValue={-70}
/>
</Content>
</Container>
Goal
You need to close all the rows, each time one of row side button clicked. The next problem is when item deleted, the next row is opened even the content is different.
How?
All you need is first, collect the ref of each rows and then when the button clicked, trigger the closeRow method of all ref's row. And the important part, make your row key persistent and unique to avoid problem like in your case.
Quick Code Sample
class Screen extends Component {
constructor(props) {
super(props);
this._rowRefs = [];
}
// this is used to collect row ref
collectRowRefs = (ref) => {
this._rowRefs.push(ref);
};
// your render row function
renderRow = (data) => (
// make this row key consistent and unique like Id, do not use index counter as key
<PersonalRankItem key={data.id} dados={data} ref={this.collectRowRefs}/>
);
// When your hidden side button is clicked
onButtonClicked = (data) => {
// do the button normal action
// ....
// close each row
this._rowRefs.forEach((ref) => {
ref.closeRow();
});
};
// this is your hidden side button
renderLeftHiddenRow = () => (
<Button onClick={this.onButtonClicked} />
);
render() {
// Your List in here
return (
<List
renderRow={this.renderRow}
renderLeftHiddenRow={this.renderLeftHiddenRow}
/>
)
}
}

React-native-popup-menu on react-navigation header

I'm using redux with react-navigation and would like to show the popup when the user clicks on the button on the react-navigation header-right button.
I wrapped the context menu at the root of my apps, as below
return (
<Provider store={store}>
<MenuContext style={{ flex: 1 }}>
<AppWithNavigationState />
</MenuContext>
</Provider>
)
in one of my screen, I have
static navigationOptions = {
headerTitle: 'News',
headerRight: (
<TouchableOpacity style={{ paddingLeft:15, paddingRight:15 }}>
<Icon name="more-vert" size={30} color="black" />
</TouchableOpacity>
),
}
When the user clicks on the right button, it should be like this
The menu items are dynamic, I will have to pull the data from one of my API and start rendering the menu data.
I've read through online it can be achieved using the context method, but I'm not sure how to implement it in my structure.
Could anyone advise me on this?
And is it possible to render it with my local variable?
The most custom way is to use Modal, when click the right button, called this.refs.modalRef.showModal(), which in your current page:
<View>
<PopupModal ref="modalRef" />
</View>
The PopupModal like this:
export default class PopupModal extends Component {
state = {
show: false,
}
showModal() {
this.setState({show: true});
}
closeModal = () => {
this.setState({show: false});
}
return (
<Modal
transparent
visible={this.state.show}
onRequestClose={this.closeModal}
>
<TouchableWithoutFeedback onPress={this.closeModal}>
<View style={{
width: '100%',
height: '100%',
opacity: 0.5,
backgroundColor: 'gray',
}} />
</TouchableWithoutFeedback>
<View></View> // your designed view, mostly position: 'absolute'
</Modal>
);
}
You can also pass some data to PopupModal by this.refs.modalRef.showModal(data), and in PopupModal:
showModal = (data) => {
this.setState({ data, show: true });
}
https://www.npmjs.com/package/react-native-material-menu
It works to me
headerRight:<View style={{marginRight:10}}>
<Menu
ref={this.setMenuRef}
button={<Text onPress={this.showMenu}><Icon style={{color:screenProps.headerTitleStyle.color,fontSize:25,marginRight:5}} name="md-more"/></Text>}
>
<MenuItem onPress={this.hideMenu}>Rate Us</MenuItem>
<MenuItem onPress={this.hideMenu}>Share App</MenuItem>
<MenuItem onPress={this.hideMenu}>Settings</MenuItem>
</Menu>
</View>,