Using Components in a Carousel - react-native

I am trying to implement a carousel in such that each item is a component in a separate file. For example...
I have Welcome.js, Welcome1.js, and Welcome2.js.
I would like to be able to swipe between each of those components. I have provided a snack demo here that reproduces my issue exactly as well as some code below.
export default class App extends React.Component {
render() {
//const data=[<Welcome/>, <Welcome1/>]
return(
<View style={{backgroundColor: '#e6e6e6', flex:1, justifyContent: 'center', alignItems: 'center', }}>
<Carousel
ref={(c) => { this._carousel = c; }}
//data={data}
/>
<Text style={{fontSize: 20}}>I would like to render a carousel that can swipe between the two components</Text>
</View>
);
}
}

It can be done with something like this -
_renderItem = ({ item, index }) => {
if (index === 0) {
return <Welcome />;
} else if (index === 1) {
return <Welcome1 />;
}
}
<Carousel
ref={(c) => { this._carousel = c; }}
data={[0,1]} // or Array(2).fill(0)
renderItem={this._renderItem}
sliderWidth={300}
itemWidth={300}
/>

Related

Reset all the state of child and parent class by a button in the parent class - React native

I've a parent class with its child classes. When the child class is clicked, its id is passed to parent class and the bg color of the child is changed.
I want to reset all the state and bgColor of child class by clicking the button in the parent class. How can I achieve it?
Thanks in advance.
Parent class:
getSelectedChilds = (id) => {
const items = this.state.selectedIds.filter(item => item.id !== id);
this.setState({
selectedIds: [...items]
});
}
render() {
return(
<View>
<FlatList
data={data}
renderItem={({ item, index }) => <Child getSelectedChilds={this.getSelectedChilds} item={item} />}
/>
<Button
onPress={() => {
// how to reset all the states (parent & child) by clicking this button?
}}
title="Submit"
/>
</View>
)
}
Child class
export default class Child extends React.Component {
constructor(props) {
super(props);
this.state = {
selectChild: false
}
}
selectedChild = (id) => {
this.setState({
selectChild: !this.state.selectChild
});
this.props.getSelectedChilds(id);
}
render() {
const { item } = this.props;
return (
<View style={{ flex: 1 }}>
<TouchableOpacity
activeOpacity={0.8}
onPress={() => {
this.selectedChild(item.id);
}}>
<View style={[{ backgroundColor: this.state.selectChild ? 'red' : 'transparent' }]}>
<View style={{ flexDirection: 'row' }}>
<Text>{item.name}</Text>
</View>
</View>
</TouchableOpacity>
</View>
)
}
}
What I did so far:
I used refs to access the function of child. It works but the state of child classes can not be changed, and hence bgColor is red all the time.
Parent class
constructor(props) {
super(props);
this.child = React.createRef();
}
resetAll = () => {
this.setState({
selectedIds: [...items]
});
this.child.current.reset(); // this is not working properly
}
render() {
return(
<View>
<FlatList
data={data}
renderItem={({ item, index }) => <Child ref={this.child} getSelectedChilds={this.getSelectedChilds} item={item} />}
/>
<Button
onPress={() => {
this.resetAll()
}}
title="Submit"
/>
</View>
)
}
Child class
reset = () => {
alert('abc'); //the alert works but the line after this doesn't work and the bg color is unchanged.
this.setState({
selectChild: false
});
}
render() {
const { item } = this.props;
return (
<View style={{ flex: 1 }}>
<TouchableOpacity
activeOpacity={0.8}
onPress={() => {
this.selectedChild(item.id);
}}>
<View style={[{ backgroundColor: this.state.selectChild ? 'red' : 'transparent' }]}>
<View style={{ flexDirection: 'row' }}>
<Text>{item.name}</Text>
</View>
</View>
</TouchableOpacity>
</View>
)
}
You could just pass a boolean in your child to tell him to reset its own state. Something like :
const resetAll = () => {
this.setState({
// ... whatever you want,
resetChild: true
});
}
And then
<Child shouldReset={this.state.resetChild} />
By cliking on your button, you will trigger a re-render in your child and this child will handle its own state.

Render conditionally header in react native

Hi I'm trying to render the header based on a state. If the state is equals to true I'm showing an avatar image and if it's false I'm rendering a default logo. I've tried doing this based on a ternary operator but it's not working. Here is the code :
static navigationOptions = ({navigation}) => {
const { params } = navigation.state;
return {
headerTitle: () => (
<View style ={{alignItems: 'center', justifyContent: 'center',flex:1, flexDirection:'column', overflow:'visible'}}>
{this.state.Loaded == false ?
<View style ={{alignItems: 'center', justifyContent: 'center',flex:1, flexDirection:'column', overflow:'visible'}}>
<Text style={{marginBottom:15,fontSize:20,fontWeight:"900", color:'#000' }}>Pseudo</Text>
<Image
style = {styles.avatar}
source = {require('../../../Assets/avatar.jpg')} />
</View>
:
<View style={[styles.bandeauHeader, { } ]}>
<Text style={styles.textHeader}>Aide</Text>
<Image source={GlobalInclude.LogoIconRose} style={styles.logoBandeauHeader} />
</View>
}
</View>
)
};
};
You should consider updating the Navigation to newer version.
First the state is not available inside the static function so you will have to use the navigation params to update the header.
The code should be something like below which you can adopt in your solution.
class DetailsScreen extends React.Component {
state = {
flag: true,
};
static navigationOptions = ({ navigation }) => {
return {
headerTitle: navigation.getParam('flag') ? (
<Text>12323231321</Text>
) : (
<Text>67676777</Text>
),
};
};
render() {
const { navigation } = this.props;
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Details Screen</Text>
<Button
title="Update the title"
onPress={() =>
this.setState({ flag: !this.state.flag }, () =>
this.props.navigation.setParams({ flag: this.state.flag })
)
}
/>
</View>
);
}
}

React native router flux; slow to load back to previous component

I'm using react-native-router-flux in my Card component to to link to my playerPage component.
This is my Card component:
class Card extends Component {
state = {
visible: false,
currentUser: ''
}
componentDidMount() {
if(this.props.player !== undefined){
axios.get(`http://localhost:4000/reports?players=${this.props.player}&startDate=2019-03-20T03:10:43.990Z&sort=date`)
.then(response => {
console.log(response)
this.props.loadCards(response.data)
})
} else if(this.props.team !== undefined) {
axios.get(`http://localhost:4000/reports?team=${this.props.team}&startDate=2019-03-20T03:10:43.990Z&sort=date`)
.then(response => {
console.log(response)
this.props.loadCards(response.data)
})
} else if(this.props.league !== undefined) {
console.log('got here')
axios.get(`http://localhost:4000/reports?league=${this.props.league}&startDate=2019-03-20T03:10:43.990Z&sort=date`)
.then(response => {
console.log(response)
this.props.loadCards(response.data)
})
} else {
axios.get(`http://localhost:4000/reports?league=NBA&league=NFL&league=MLB&startDate=2019-03-20T03:10:43.990Z&sort=date`)
.then(response => {
this.props.loadCards(response.data)
})
Auth.currentAuthenticatedUser()
.then((data) => {
this.props.loadFilters(data.attributes.sub)
this.setState({currentUser: data})
})
}
}
render() {
let cardValues = this.props.search === null ? this.props.card : this.props.search
return (
<View >
{
cardValues != null ?
cardValues.map((v,i) => {
return(
<View key={i}>
<Collapse
>
<CollapseHeader
>
<View>
<Text>
<Icon
name={this.iconName(v.player.team.league.acronym)}
size={12}
color='black'/>{' '}
<Text
onPress={
()=> {
Actions.playerPage({
player: v.player._id
})
}
}
>{v.player.player_name} - </Text>
</Text>
<Text>
<View>
</View>
</View>
<View>
</View>
</CollapseHeader>
</Collapse>
</View>
)
})
: null
}
</View>
)
}
}
export default connect(mapStateToProps, { loadCards, countMore, loadFilters })(Card))
This is my playerPage component:
PlayerPage = (props) => {
return(
<View>
<Header
rounded
>
<View style={{flexDirection: 'row', flexWrap: 'wrap', right: '43%', top: '50%', paddingBottom: 900}}>
<Icon name='chevron-left' size={10} color='#006FFF' />
<NativeText
onPress={() => {Actions.fullApp()}}
style ={{color: '#006FFF', fontSize: 12, fontFamily: 'Montserrat-Regular', top: '900%' }}
>
Back
</NativeText>
</View>
</Header>
<Card
player={props.player}
team={props.team}
league={props.league}
/>
</View>
)
}
export default PlayerPage
When I link to playerPage I render a new version of Card on playerPage.
The data that is shown on playerPage is determined by the API call in the componentDidMount of the Card component.
I initially direct to playerPage with onPress={ ()=> {Actions.playerPage({player: v.player._id})}}
This loads fine.
When I direct a user back to the fullApp component, which has the Card component on it, the data loads, but much more slowly.
This is what it looks like:
https://streamable.com/ugc0b
Any ideas why it loads slowly? That's my issue.

Better solution to open the Menu when 3 dots are clicked in React Native

I am able to open menu when 3 dots icon is clicked for each item. But can the code be written in a better way..
Right now menu is getting created for each card item but ideally it would have been good to create single Menu View and dynamically associate it to some card where ever the 3 dots is clicked.
Expo Source Code Link
Code
export default class App extends React.Component {
constructor(props, ctx) {
super(props, ctx);
this.state = {
list: [
{ name: "Michael", mobile: "9292929292", ref: React.createRef() },
{ name: "Mason Laon Roah", mobile: "1232313233", ref: React.createRef() },
{ name: "Constructor", mobile: "4949494949", ref: React.createRef() },
{ name: "Rosling", mobile: "4874124584", ref: React.createRef() }
],
};
}
_menu = null;
hideMenu = () => {
this._menu.hide();
};
showMenu = (ref) => {
this._menu = ref;
this._menu.show();
};
render() {
const renderItem = ({ item, index }) => (
<ListItem
title={
<View>
<Text style={{ fontWeight: "bold" }}>{item.name}</Text>
<Text>{item.mobile}</Text>
</View>
}
subtitle={
<View>
<Text>445 Mount Eden Road, Mount Eden, Auckland. </Text>
<Text>Contact No: 134695584</Text>
</View>
}
leftAvatar={{ title: 'MD' }}
rightContentContainerStyle={{ alignSelf: 'flex-start'}}
rightTitle={this.getMenuView(item.ref)}
/>
);
return (
<View style={styles.container}>
<View style={{ flex: 1, marginTop: 30 }}>
<FlatList
showsVerticalScrollIndicator={false}
keyExtractor={(item, index) => index.toString()}
data={this.state.list || null}
renderItem={renderItem}
ItemSeparatorComponent={() => (
<View style={{ marginBottom: 5 }} />
)}
/>
</View>
</View>
);
}
getMenuView(ref) {
return (
<Menu
ref={ref}
button={<Icon onPress={() => this.showMenu(ref.current)} type="material" color="red" name="more-vert" />}
>
<MenuItem onPress={this.hideMenu}>Menu item 1</MenuItem>
<MenuItem onPress={this.hideMenu}>Menu item 2</MenuItem>
<MenuItem onPress={this.hideMenu} disabled>
Menu item 3
</MenuItem>
<MenuDivider />
<MenuItem onPress={this.hideMenu}>Menu item 4</MenuItem>
</Menu>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#ecf0f1',
padding: 8,
},
});
Sample Output
As mentioned here, you can find an undocumented UIManager.java class that allows you to create Popups with its showPopupMenu method.
This currently works only for Android.
import React, { Component } from 'react'
import { View, UIManager, findNodeHandle, TouchableOpacity } from 'react-native'
import Icon from 'react-native-vector-icons/MaterialIcons'
const ICON_SIZE = 24
export default class PopupMenu extends Component {
constructor (props) {
super(props)
this.state = {
icon: null
}
}
onError () {
console.log('Popup Error')
}
onPress = () => {
if (this.state.icon) {
UIManager.showPopupMenu(
findNodeHandle(this.state.icon),
this.props.actions,
this.onError,
this.props.onPress
)
}
}
render () {
return (
<View>
<TouchableOpacity onPress={this.onPress}>
<Icon
name='more-vert'
size={ICON_SIZE}
color={'grey'}
ref={this.onRef} />
</TouchableOpacity>
</View>
)
}
onRef = icon => {
if (!this.state.icon) {
this.setState({icon})
}
}
}
Then use it as follows.
render () {
return (
<View>
<PopupMenu actions={['Edit', 'Remove']} onPress={this.onPopupEvent} />
</View>
)
}
onPopupEvent = (eventName, index) => {
if (eventName !== 'itemSelected') return
if (index === 0) this.onEdit()
else this.onRemove()
}
Source: https://cmichel.io/how-to-create-a-more-popup-menu-in-react-native
There is now a React Native plugin for this. I'm not sure it was around when the question was originally asked. But I'm leaving this here for anyone else looking for the answer.
https://www.npmjs.com/package/react-native-popup-menu
The example worked for me. I wanted to use the vertical ellipsis, so I did this modification to the MenuTrigger part of the example to an icon instead of text:
<MenuTrigger>
<Icon name="more-vert" size={25} color={colors.rustRed} />
</MenuTrigger>
As a side note, I had difficulty finding and using the ellipsis. I eventually went with using react-native-vector-icons by using 'npm -i react-native-vector-icons' and importing the Material Icons like this:
import Icon from 'react-native-vector-icons/MaterialIcons';
Use React Portals
https://reactjs.org/docs/portals.html
In short the receipts is:
You define your dynamic menu at sibling level only once in the parent i.e. in your case it would be adjacent to App.
Handle Click at each item level to open your component. You can pass some specific event days to achieve the dynamism.
Easier example https://codeburst.io/reacts-portals-in-3-minutes-9b2efb74e9a9
This achieves exactly what you are trying to do which is defer the creation of component untill clicked.

How to present vertical separators on a horizontal `FlatList`?

I’m using React Native’s new FlatList component. I’m using it to present an horizontal list, but when using the built-in ItemRenderComponent it presents the separators beneath each items, instead of in between.
Is there a way to change that?
interface State {
dataSource: WhosOutByPolicy[];
}
interface Props {
data: WhosOutByPolicy[];
}
class WhosOutParentCell extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { dataSource: props.data };
}
renderSeparator = () => {
return (
<View
style={{
height: 100,
width: 3,
backgroundColor: "#D81458"
}}
/>
);
};
render() {
return (
<View style={styles.container}>
<FlatList
data={this.state.dataSource}
horizontal={true}
renderItem={({ item }) => (
<WhosOutUsersInPolicyCell data={item} />
)}
keyExtractor={item => item.policyDispalyName}
ItemSeparatorComponent={this.renderSeparator}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#EFEFEF"
}
});
export default WhosOutParentCell;
It is a react-native bug that is not fixed yet. You can rewrite your code as blow to solve this problem:
renderSeparator = () => {
return (
<View
style = {{
height: 100,
width: 3,
backgroundColor: '#D81458'
}}
/>
)
}
_renderItem = ({item, index}) => (
<View style={{flexDirection: 'row'}}>
<WhosOutUsersInPolicyCell data = { item } />
{(index !== this.state.dataSource.length - 1) && this.renderSeparator()}
</View>
)
render() {
return (
<View style = { styles.container } >
<FlatList
data = { this.state.dataSource }
horizontal = { true }
renderItem = {this._renderItem}
keyExtractor = { item => item.policyDispalyName }
/>
</View>
)
}