im using PanResponder in React Native but i have a simple problem that i can't solve!!!
this is my code
class test extends React.Component {
constructor(props) {
super(props);
const position = new Animated.ValueXY();
const panResponder = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onPanResponderMove: (evt, gs) => {
console.log(gs.dx);
position.setValue({ x: gs.dx , y: gs.dy })
},
onPanResponderRelease: (evt, gs) => {
},
});
this.state = { panResponder, position }
}
getCardStyle() {
const { position } = this.state;
return {
...position.getLayout(),
};
}
render() {
return (
<View>
{
this.props.cart.map((item, index) => {
return (
<Animated.View style={[this.getCardStyle() ,styles.CardContainer]} key={item.id}
{...this.state.panResponder.panHandlers}
>
<View><Image style={styles.image} source={{ uri: item.src}} /></View>
<Text style={styles.title}>{item.location}</Text>
</Animated.View>
)
})
}
</View>
);
}
}
i want to move every item finger
Separately but when i touch every item in list whole items start moving.
can anyone help me please??
SOLVE IT
I made another component named ListItem and in that component render FlatList items and it worked!
Related
React Native WebView App not exit on pressing back button after setting Go back functionality on back button pressed. I want go back functionality on pressing back button when webview is not on home page and when webview is on home page then exit the app.
export default class WebView extends Component {
constructor (props) {
super(props);
this.WEBVIEW_REF = React.createRef();
}
componentDidMount() {
BackHandler.addEventListener('hardwareBackPress', this.handleBackButton);
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton);
}
handleBackButton = ()=>{
this.WEBVIEW_REF.current.goBack();
return true;
}
onNavigationStateChange(navState) {
this.setState({
canGoBack: navState.canGoBack
});
}
render(){
return (
<WebView
source={{ uri: 'https://stackoverflow.com' }}
ref={this.WEBVIEW_REF}
onNavigationStateChange={this.onNavigationStateChange.bind(this)}
/>
);
}
}
Since you are managing the state of canGoBack inside onNavigationStateChange function, Change your handleBackButton function as below,
handleBackButton = () => {
if (this.state.canGoBack) {
this.WEBVIEW_REF.current.goBack();
return true;
}
}
Check below complete example
import React, { Component } from "react";
import { BackHandler } from "react-native";
import { WebView } from "react-native-webview";
export default class App extends Component {
WEBVIEW_REF = React.createRef();
state = {
canGoBack: false,
};
componentDidMount() {
BackHandler.addEventListener("hardwareBackPress", this.handleBackButton);
}
componentWillUnmount() {
BackHandler.removeEventListener("hardwareBackPress", this.handleBackButton);
}
handleBackButton = () => {
if (this.state.canGoBack) {
this.WEBVIEW_REF.current.goBack();
return true;
}
};
onNavigationStateChange = (navState) => {
this.setState({
canGoBack: navState.canGoBack,
});
};
render() {
return (
<WebView
source={{ uri: "https://stackoverflow.com" }}
ref={this.WEBVIEW_REF}
onNavigationStateChange={this.onNavigationStateChange}
/>
);
}
}
Hope this helps you. Feel free for doubts.
I had this problem for quite a while but i have managed to resolve it. Problem that I experienced was that goBack (which is used as back event) function was triggered before onNavigationStateChange but somehow state was change although goBack function was called first.
const HomeScreen = () => {
const {web} = config;
const ref = useRef();
const [canGoBack, setCanGoBack] = useState(false);
const setupState = event => {
setCanGoBack(event?.canGoBack);
};
useEffect(() => {
const goBack = () => {
if (canGoBack === false) {
Alert.alert(
'Exit App',
'Do you want to exit app?',
[
{text: 'No', onPress: () => console.log('No'), style: 'cancel'},
{text: 'Yes', onPress: () => BackHandler?.exitApp()},
],
{cancelable: false},
);
}
ref?.current?.goBack();
return true;
};
BackHandler?.addEventListener('hardwareBackPress', () => goBack());
return () =>
BackHandler?.removeEventListener('hardwareBackPress', () => goBack());
}, [canGoBack]);
return (
<View style={styles.mainContainer}>
{/* last version 11.21.1 */}
<WebView
ref={ref}
source={{uri: web?.url}}
style={{flex: 1}}
cacheEnabled={web.cacheEnabled}
automaticallyAdjustContentInsets={false}
domStorageEnabled={true}
startInLoadingState={true}
allowsInlineMediaPlayback={true}
allowsBackForwardNavigationGestures
onNavigationStateChange={e => setupState(e)}
/>
</View>
);
};
export default HomeScreen;
Here is what i try i use setInterval function to set a variable content will be changed every second and i find onMomentumScrollEnd can get the position y when scroll the FlatList
And then i am stuck , i thougt event.nativeEvent.contentOffset.y = this.state.content; can let my FlatList automatic scroll. Obviously it is not.
Any one can give me some suggestion ? Thanks in advance.
My data is from an API
Here is my App.js:
import React from 'react';
import { View, Image, FlatList, Dimensions } from 'react-native';
const { width, height } = Dimensions.get('window');
const equalWidth = (width / 2 );
export default class App extends React.Component {
constructor(props) {
super(props);
this.renderRow = this.renderRow.bind(this);
this.state = { movies: [], content: 0 };
}
componentWillMount() {
fetch('https://obscure-reaches-65656.herokuapp.com/api?city=Taipei&theater=Centuryasia')
.then(response => response.json())
.then(responseData => {
console.log(responseData);
this.setState({ movies: responseData[0].movie });
})
.catch((error) => console.log(error));
this.timer = setInterval(() => {
this.setState({content: this.state.content+1 })
}, 1000);
}
// get the jsonData key is item and set the value name is movie
renderRow({ item: movie }) {
console.log('renderRow => ');
return (
<View>
<Image source={{ uri: movie.photoHref}} style={{ height: 220, width: equalWidth }} resizeMode="cover"/>
</View>
);
}
render() {
const movies = this.state.movies;
// it well be rendered every second from setInterval function setState
console.log('render');
return (
<View style={{ flex: 1 }}>
<FlatList
data={movies}
renderItem={this.renderRow}
horizontal={false}
keyExtractor={(item, index) => index}
numColumns={2}
onMomentumScrollEnd={(event) => {
console.log(event.nativeEvent.contentOffset.y);
event.nativeEvent.contentOffset.y = this.state.content;
}}
/>
</View>
);
}
}
You need to tell your FlatList that you want it to scroll to a new position using scrollToOffset().
Store a reference to your FlatList in your class by adding the prop
ref={flatList => { this.flatList = flatList }} to it.
Then, call this.flatList.scrollToOffset({ offset: yourNewOffset }) to scroll to the desired offset.
Docs on this method are here.
I want to make a responsive App in React Native. I subscribe to Dimension changes in the container using this:
const RCTDeviceEventEmitter = require("RCTDeviceEventEmitter");
export interface Props {
data: Array<any>;
}
export interface State {}
class MyContainer extends React.Component<Props, State> {
_updateIfSelected() {
if (...some more logic...) {
this.forceUpdate();
}
}
constructor(props) {
super(props);
this.state = {
listener: null,
updateIfSelected: this._updateIfSelected.bind(this),
};
}
componentWillMount() {
this.setState({
listener: RCTDeviceEventEmitter.addListener("didUpdateDimensions", this.state.updateIfSelected),
});
}
componentWillUnmount() {
this.state.listener.remove("didUpdateDimensions",this.state.updateIfSelected);
}
renderItem() {
console.log("Rerendering Item")
return <Text style={{ width: Dimensions.get("window").width }} >Some Text</Text>
}
render() {
return (
<FlatList
data={this.props.data}
keyExtractor={(_, i) => (i.toString())}
renderItem={({item}) => this.renderItem(item)}
/>
}
}
I was wondering how to force a FlatList to rerender its items, because the appearance needs to change when the screen is tilted. However, because the data doesn't change, the list won't be rerendered on screen tilts.
The Documentation provides a parameter called extraData:
By passing extraData={this.state} to FlatList we make
sure FlatList itself will re-render when the state.selected
changes.
Without setting this prop, FlatList would not know it
needs to re-render any items because it is also a
PureComponent and the prop comparison will not show any changes
But I don't really understand what this is trying to say. Any Ideas how I can make the FlatList Rerender on Dimension changes?
You can pass a function to onLayout and check the dimensions when onLayout is called. I would recommend passing the function to a parent view. You could then setState in the function
class MyComponent extends Component {
_onLayout() { // I haven't tried this but I think this or something similar will work
const { width, height } = Dimensions.get('window');
if(width > height) {
this.setState(state => ({ ...state, orientation: 'landscape' }));
} else {
this.setState(state => ({ ...state, orientation: 'portrait' }));
}
}
render() {
return (
<View onLayout={this._onLayout}>
<FlatList
data={this.props.data}
keyExtractor={(_, i) => (i.toString())}
renderItem={({item}) => this.renderItem(item)}
extraData={this.state.orientation}
/>
</View>
)
}
}
I want to implement infinite scrollview in both the direction. Also the data should be loaded dynamically.
I am using SectionList component for list. I have implemented forward infinite scrolling. That means if user scroll down, the data will append to list automatically.
For that I have used onMomentumScrollEnd event. When user stops the scrolling, if the scroll is in Up direction, data will be appended at the End and if the scroll is in Down direction, data will be appended at the Top.
Now the problem is when I append the data at Top of list, It shift all the current list data to backward. I don't want to shift the current list even if the data is updated. Is there any way to do it.
This is my code:
import React, {Component} from 'react';
import {
Text,
View,
StyleSheet,
SectionList,
} from 'react-native';
import CardComponent from './CardComponent'
export default class Schedule extends Component {
constructor(props) {
super(props);
this.state = {
sectionData: [],
loading: false,
}
this.contentOffsetY = 0;
this._onScroll = this._onScroll.bind(this)
}
componentDidMount() {
this.setState({ sectionData: this.props.data })
}
renderItem = ({item}) => (
<CardComponent
data={item}
key={item}
/>
);
renderDateSeparator(text) {
return (
<Text style={{
paddingVertical: 15,
fontSize: 14,
flex: 1,
textAlign: 'center',
textAlignVertical: 'center',
}}>
{text}
<Text>
)
}
_onScroll(e){
let contentOffset = e.nativeEvent.contentOffset.y;
this.contentOffsetY < contentOffset ? this.loadMoreOnBottom() : this.loadMoreOnTop();
this.contentOffsetY = contentOffset;
}
loadMoreOnTop() {
this.setState({ lodaing: true });
// code to append data on top of list
this.setState({ lodaing: false });
}
loadMoreOnBottom() {
// code to append data at bottom of list
}
render() {
const sectionData = this.state.sectionData;
return(
<View style={{flex: 1}}>
<SectionList
onMomentumScrollEnd={this._onScroll}
automaticallyAdjustContentInsets={false}
itemShouldUpdate={false}
renderItem={this.renderItem}
renderSectionHeader={({section}) => this.renderDateSeparator(section.date)}
sections={sectionData}
stickySectionHeadersEnabled={false}
refreshing={this.state.loading}
onRefresh={() => this.loadMoreOnTop()}
onEndReachedThreshold={0.3}
onEndReached={() => this.loadMoreOnBottom()}
keyExtractor={(item) => item.key}
/>
</View>
)
}
}
Thanks in advance.
After so much of research, I have finally implemented the bidirectional infinite scroll view in react-native.
For the implementation, I have replaced my SectionList with FlatList, Because I want to use scrollToOffset method which is not properly working in SectionList.
I have used setInterval function of javaScript. It regularly checks weather the list need to be append from top or bottom.
This is my code:
import React, {Component} from 'react';
import {
Text,
View,
StyleSheet,
FlatList,
Dimensions,
} from 'react-native';
import CardComponent from './CardComponent'
let {height, width} = Dimensions.get('window');
export default class Schedule extends Component {
constructor(props) {
super(props);
this.state = {
listData: [],
}
this.contentOffsetY = 0;
this.pageOffsetY = 0;
this.contentHeight = 0;
this._onScroll = this._onScroll.bind(this);
this.loadMoreOnTop = this.loadMoreOnTop.bind(this);
this.loadMoreOnBottom = this.loadMoreOnBottom.bind(this);
}
componentDidMount() {
this.setState({ listData: this.props.data });
this._interval = setInterval(() => {
this.setState({ load: true });
}, 2000);
}
componentWillUnmount() {
clearInterval(this._interval);
}
renderItem = ({item}) => (
<CardComponent
data={item}
key={item}
/>
);
_onScroll(e){
let contentOffset = e.nativeEvent.contentOffset.y;
this.contentOffsetY < contentOffset ? this.loadMoreOnBottom() : this.loadMoreOnTop();
this.contentOffsetY = contentOffset;
}
scrollToOffset = (offset) => {
this.flatListRef ? this.flatListRef.scrollToOffset({animated: false, offset}) : null;
};
loadMoreOnTop() {
let newOffset;
// code to append data on top of list
// calculate newOffset:
newOffset = this.pageOffsetY + space required for new data.
this.contentOffsetY = newOffset;
this.scrollToOffset(newOffset);
}
loadMoreOnBottom() {
// code to append data at bottom of list
}
render() {
const listData = this.state.listData;
if(this.pageOffsetY < 600) {
this.loadMoreOnTop();
} else if((this.contentHeight - this.pageOffsetY) < (height * 1.5)){
this.loadMoreOnBottom();
}
return(
<View style={{flex: 1}}>
<FlatList
onScroll={(e) => {
this.pageOffsetY = e.nativeEvent.contentOffset.y;
this.contentHeight = e.nativeEvent.contentSize.height;
return null;
}}
onMomentumScrollEnd={this._onScroll}
automaticallyAdjustContentInsets={false}
itemShouldUpdate={false}
renderItem={this.renderItem}
data={listData}
refreshing={false}
onRefresh={() => this.loadMoreOnTop()}
onEndReachedThreshold={0.3}
onEndReached={() => this.loadMoreOnBottom()}
keyExtractor={(item) => item.key}
ref={(ref) => { this.flatListRef = ref; }}
animated={false}
/>
</View>
)
}
}
I've tried flatlist but it has a bit of performance issues in android.
As I scroll down, it loads the list. But afterwards, it shows blank while scrolling upwards.
After reaching the end of the screen, it stops for a while and then loads the datas. Why is it not showing loader (activity indicator) at the bottom? Why is onEndReached and onEndReachedThreshold not working?
Plz have a look at the video here
https://youtu.be/5tkkEAUEAHM
My code:
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
FlatList,
ActivityIndicator,
} from 'react-native';
import { List, ListItem, SearchBar } from "react-native-elements";
export default class FlatListExample extends Component
{
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
page: 1,
seed: 1,
error: null,
refreshing: false,
};
}
componentDidMount() {
this.makeRemoteRequest();
}
makeRemoteRequest = () => {
const { page, seed } = this.state;
const url = `https://randomuser.me/api/?seed=${seed}&page=${page}&results=20`;
console.log('url', url);
this.setState({ loading: true });
setTimeout(()=>{
fetch(url)
.then(res => res.json())
.then(res => {
this.setState({
data: [...this.state.data, ...res.results],
error: res.error || null,
loading: false,
refreshing: false
});
})
.catch(error => {
this.setState({ error, loading: false });
});
},0);
};
renderFooter = () => {
if (!this.state.loading) return null;
return (
<View
style={{
paddingVertical: 20,
borderTopWidth: 1,
borderColor: "#CED0CE"
}}
>
<ActivityIndicator animating size="large" />
</View>
);
};
handleLoadMore = () =>{
this.setState({
page:this.state.page + 1,
},()=>{
this.makeRemoteRequest();
})
}
render() {
return (
<FlatList
data={this.state.data}
renderItem={({ item }) => (
<ListItem
roundAvatar
title={`${item.name.first} ${item.name.last}`}
subtitle={item.email}
avatar={{ uri: item.picture.thumbnail }}
/>
)}
keyExtractor={item => item.email}
ListFooterComponent={this.renderFooter}
onEndReached={this.handleLoadMore}
onEndReachedThreshold={50}
/>
);
}
}
AppRegistry.registerComponent('FlatListExample', () => FlatListExample);
I've noticed that you're not setting initialNumToRender. From the docs:
initialNumToRender: number
How many items to render in the initial batch. This should be enough
to fill the screen but not much more. Note these items will never be
unmounted as part of the windowed rendering in order to improve
perceived performance of scroll-to-top actions.
So you'll want to estimate how many cells you expect to be visible at any given time and set it to that. I'd also recommend if you haven't already to update to the latest react-native which includes various improvements on the FlatList component.