I use the new API <SectionList/> to build a list UI, but I found issues running on Android devices.
While scrolling down with SectionList, only a few new cells/rows can be displayed at once. You need to scroll SectionList down again to display the next a few new cells/rows, and wait for the next cells are ready to be displayed. It works not the same on the iOS.
When reaching the last item in the SectionList, I tried to scroll up quickly to the first item in the SectionList, and it display a blank view with a few seconds till the cells are rendered.
I have hundred rows (country list) for display, and the user experience is bad. The user takes many seconds to reach the last item in the country list, and takes many seconds to show the previous rows after scrolling up quickly.
export default class LoginCountryList extends React.Component {
props: LoginCountryListProps;
static defaultProps = {
data: {},
};
renderRow = (rowItem: any) => {
console.log('render row:');
return (
<TouchableHighlight
onPress={() => this.props.onSelectRow(rowItem.item)}
>
<View style={styles.row}>
<Text style={styles.rowLeftText}>{rowItem.item.name}</Text>
<Text style={styles.rowRightText}>{`+${rowItem.item.ccc}`}</Text>
</View>
</TouchableHighlight>
)
};
renderSectionHeader = (sectionItem: any) => (
<View
style={styles.sectionHeader}
>
<Text style={styles.sectionHeaderText}>{sectionItem.section.title}</Text>
</View>
);
render() {
console.log('re-render section list')
return (
<View style={styles.container}>
<NavigationBar.TextStyle
title={title}
leftText={leftText}
onLeft={this.props.onBack}
/>
<SectionList
style={styles.listView}
sections={this.props.data}
renderItem={this.renderRow}
renderSectionHeader={this.renderSectionHeader}
/>
</View>
);
}
}
// data:
const data = [
// first section
{
"title": "A",
"key": "A",
"data": [
// first row
{
"ccc": "93",
"countryCode": "AF",
"currencyCode": "AFN",
"currencySymbol": "؋",
"name": "Afghanistan",
"key": "AF"
},
{
"ccc": "355",
"countryCode": "AL",
"currencyCode": "ALL",
"currencySymbol": "Lek",
"name": "Albania",
"key": "AL"
},
...
]
},
...
]
I tried to log in the renderItem callback when SectionList did display, and it calls renderItem hundred times for displaying only 15 rows in the first section.
Log a constant string render row: with hundred times when the SectionList did display:
Log the indexes of the items when the SectionList did display. The same row did render multiple times when the SectionList did display.
Did use SectionList wrongly? How do I do to fix it?
Environment
react-native -v: 0.45.1 & 0.46.1 (tried both version)
node -v: v7.8.0
npm -v: 4.2.0
yarn --version (if you use Yarn): 0.22.0
Target Platform (e.g. iOS, Android): Android
Development Operating System (e.g. macOS Sierra, Windows 10): macOS
Build tools (Xcode or Android Studio version, iOS or Android SDK version, if relevant): Android SDK 25.0.2
Related
I have a list of cards. I am selecting a sublist from the cards and transforming their x and y location to another point in the screen. I am trying to update their zIndex using a shared variable that updates based upon the order I select them. However, this works fine on Android but not on iOS. (Please look at the image for visual clarification).
Visual representation of ideal scenario
This is a simplified code of what I have.
This is the render component.
<View>
<TapGestureHandler onGestureEvent={tapGestureEvent}>
<Animated.View style={[cardStyle]}>
<Animated.View style={[frontImageStyle]}>
<Image
source={require('../../../assets/frontImage.png')}
style={styles.card}
/>
</Animated.View>
<Animated.View style={[backImageStyle]}>
<Image
source={require('../../../assets/backImage.png')}
style={styles.card}
/>
</Animated.View>
</Animated.View>
</TapGestureHandler>
</View>
This is my gestureTapHander where I am updating the x and y locations and also updating the index for each card based upon the total cards drawn so far, this total cards drawn is a shared variable and defined in my parent component.
const tapGestureEvent = useAnimatedGestureHandler({
onStart: (event, context) => {
context.y = yPosition.value; // It seems I am not even using this.
},
onActive: (event, context) => {},
onEnd: (event, context) => {
newYPosition.value = someValue
cardDrawnIndex.value = cardsDrawn.value;
cardsDrawn.value += 1;
},
});
This is the style that I have on the outerComponent.
cardDrawnIndex = useSharedVariable(-1);
const cardStyle = useAnimatedStyle(() => {
return {
transform: [
{translateX: someXValue},
{translateY: someYValue},
{rotateZ: `${someVariable.value}deg`},
{rotateY: `${someVariable.value}deg`},
{translateX: wasToChangeAnchorPointForMyZRotation}, // I don't understand why there is a need to do it and why isn't it as simple as saying, rotate with this anchor point.
{translateY: wasToChangeAnchorPointForMyZRotation},
],
zIndex: cardDrawnIndex.value,
// elevation: drawnCardZPosition.value, // Also tried this, it works only for Android, not for iOS.
};
});
I see there is a transformZ utility in react-native-redash but I am unable to figure out how to use it correctly for my use case. Or alternatively, if I can fix this zIndex issue on iOS, (maybe it has to do something with creating new instances of my card.)
I'm building a mobile app using react-native.
The main screen is a combination of different classes (different js files) that fetches different json urls.
I need to refresh index.js every time the user pulls down the screen.
How can I do this without listviews or flatviews?
Any idea?
You can use npm for the same.
1) install npm i react-native-pull-to-refresh --save
2) Then use below code for the refresh.
`import PTRView from 'react-native-pull-to-refresh';
var PullToRefreshProject = React.createClass({
_refresh: function() {
return new Promise((resolve) => {
setTimeout(()=>{resolve()}, 2000)
});
},
render: function() {
return (
<PTRView onRefresh={this._refresh} >
<View style={styles.container}>
<Text style={styles.welcome}>
Let's Pull!
</Text>
</View>
</PTRView>
);
},
});`
Ref link:- https://www.npmjs.com/package/react-native-pull-to-refresh
This is my JSON file
{
"name": "Thịt bò",
"noed": 5
},
{
"name": "Thịt heo",
"noed": 3
}
I get them to Flatlist
<FlatList
data={cats}
keyExtractor={item => item.name}
renderItem={({item})=>(
<View>
<Text style={styles.catsItem} onPress={() => this.changeTextInput(item.name)}>{item.name} {item.noed}</Text>
</View>
)}
/>
But I want to send 2 values are item.name and item.noed to TextInput then send them to another screen
changeTextInput(item){
this.setState({name: item});
};
But I don't know how to send item.noed to TextInput and how to send them to another screen.
I'm quite new, so please help me.
Use react-navigation npm package to redirect from one screen to another screen you can also pass values
There are two pieces to this:
Pass params to a route by putting them in an object as a second parameter to the navigation.navigate function:
this.props.navigation.navigate
('RouteName', { /* params go here */ })
onPress={()=>this.props.navigation.navigate('Details',
{ itemId: 86, otherParam: 'anything you want here', })};
Read the params in your screen component:
this.props.navigation.getParam(paramName, defaultValue)
For more information please read the following document
React Navigation Link
I have set custom style url in map initialization. Like :
<Mapbox.MapView
styleURL="asset://mystyle.json"
logoEnabled={false}
attributionEnabled={false}
ref={(e) => { this.oMap = e }}
animate={true}
zoomLevel={6}
centerCoordinate={[54.0, 24.0]}
style={{ flex: 1 }}
showUserLocation={true}>
</Mapbox.MapView>
In mystyle.json I have two base map as below :
{
"id": "Satellite",
"type": "raster",
"source": "Satellite",
"layout": {
"visibility": "visible"
},
"paint": {
"raster-opacity": 1
}
},
{
"id": "Satellite2",
"type": "raster",
"source": "Satellite",
"layout": {
"visibility": "none"
},
"paint": {
"raster-opacity": 1
}
}
Satellite is visible default.
How to set visibility of satellite property to none and satellite2 visibility to visible at run time?
Mapbox gl :
"#mapbox/react-native-mapbox-gl": "^6.1.3"
React native :
"react-native": "0.58.9",
Finally I got solution :
constructor() {
this.state = {
lightMap: 'visible',
darkMap: 'none'
};
}
changeMap(){
this.setState({darkMap:'visible'})
}
<MapboxGL.MapView
styleURL="asset://mystyle.json"
logoEnabled={false}
attributionEnabled={false}
ref={(e) => { this.oMap = e }}
zoomLevel={6}
centerCoordinate={[54.0, 24.0]}
style={{ flex: 1 }}>
<MapboxGL.RasterSource
id="idLightMap"
url="LAYERURL1"
tileSize={256}>
<MapboxGL.RasterLayer
id="idLightMap"
sourceID="idLightMap"
style={{visibility: this.state.lightMap}}>
</MapboxGL.RasterLayer>
</MapboxGL.RasterSource>
<MapboxGL.RasterSource
id="idDarkMap"
url="LAYERURL2"
tileSize={256}>
<MapboxGL.RasterLayer
id="idDarkMap"
sourceID="idDarkMap"
style={{visibility: this.state.darkMap}}>
</MapboxGL.RasterLayer>
</MapboxGL.RasterSource>
</MapboxGL.MapView>
I have added raster layer and programmatic toggling it.
let say we have one state isStateliteVisible:false,
now change this state to true when you want to visibility
and use mapbox like this,
<Mapbox.MapView
styleURL={this.state.isStateliteVisible?...visiblityStyle:....noneStyle} // use this as per your case
logoEnabled={false}
attributionEnabled={false}
ref={(e) => { this.oMap = e }}
animate={true}
zoomLevel={6}
centerCoordinate={[54.0, 24.0]}
style={{ flex: 1 }}
showUserLocation={true}>
</Mapbox.MapView>
I can see that you are using a older depreciated version of mapbox-gl.
This package is deprecated please use this instead.
Installation
Dependencies
node
npm
React Native recommended version 0.50 or greater
Git
git clone git#github.com:mapbox/react-native-mapbox-gl.git
cd react-native-mapbox-gl
Yarn
yarn add #mapbox/react-native-mapbox-gl
Npm
npm install #mapbox/react-native-mapbox-gl --save
You're good to go!
I am switching from android to react native. Complete naive.
I wanted to implement something like recyclerview in react native and found out about FLATLIST
Now the problem is initially my data variable is empty and later on i am adding data into that variable. Now how do i notify the flat list that the data has changed and it should now re render itself.
Like in recyclerview we use adapter.notifyDataSetChanged(); to inform the recycler view about the change that it should re-render itself now
The code i am using is
export default class Convo extends Component{
constructor(props){
super(props);
this.state = {
loggedIn: 'false',
title: 'Login/SignUp',
messages: []
};
this.downloadConversation = this.downloadConversation.bind(this);
}
componentDidMount(){
this.downloadConversation();
}
downloadConversation(){
this.setState(
{message: [
{
key: "HIHI",
name: "lets this bullshit",
message: "I i i"
},
{
key: "HIHI2",
name: "lets change bullshit",
message: "I i i"
},
{
key: "HIHI3",
name: "lets change this ",
message: "I i i"
}
]}
);
//After updating the messages object, i want to notify flat list about
//the change, basically i will be updating this object asynchronously
// in background which will take time. for now i am updating directly
//to show you
}
renderFlatListItem(item) {
return (
<View key={item.key} style={styles1.flatviewItem}>
<Text>User1</Text>
<Text style={styles1.name}>{item.name}</Text>
<Text style={styles1.messageStyle}>{item.message}</Text>
</View>
)
}
render(){
return(
<View style={styles1.container}>
<View style={styles1.header}>
<Text style={styles1.h2style}>Conversation List</Text>
</View>
<FlatList
style={styles1.flatview}
extraData={this.state}
keyExtractor={item=>item.key}
showsVerticalScrollIndicator={true}
data={this.state.messages}
renderItem={({item}) => this.renderFlatListItem(item)}
/>
</View>
);
}
}
Your component should automatically re-render when the component state changes (if anything in your render method references the changed piece of state). I think you just need to change 'message' to 'messages' when you setState in your downloadConversation() method. Your FlatList is looking for this.state.messages, not this.state.message and this.state.messages is never changed. Just fix that typo & hopefully that fixes it.