My onPress handler is not working when someone clicks on Flatlist item.
Video of this issue
https://u.pcloud.link/publink/show?code=XZWGOUkZmDLPeKQOQJJzxnqFB8Q21X3acT7k
Here is the code:
import React, { useState, useEffect } from 'react';
import { View, Text, Image, FlatList, ActivityIndicator } from 'react-native';
import { TouchableNativeFeedback } from 'react-native-gesture-handler';
import axios from 'axios';
export default function _AjaxApp() {
const [postList, setPostList] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const [isLoading, setIsLoading] = useState(false);
const loadData = (append = false) => {
let url = "https://edristi.in/wp-json/wp/v2/posts?per_page=20&page=" + currentPage;
setIsLoading(true);
setCurrentPage(currentPage + 1);
axios.get(url).then((r) => {
if (append) {
setPostList(postList.concat(r.data));
} else {
setPostList(r.data);
}
setIsLoading(false);
}).catch((e) => {
console.log(e);
});
}
useEffect(() => {
loadData();
}, [])
let Loader = <></>
if (isLoading) {
Loader = <ActivityIndicator></ActivityIndicator>
}
return (
<View>
<View style={{padding:20, backgroundColor:"#4342fe"}}>
<Text style={{color:"white"}}>Edristi App</Text>
</View>
<FlatList
data={postList}
renderItem={({ item, index, separators }) => <PostCard postList={postList} {...item} index={index} />}
keyExtractor={r => r.id + "-" + Math.random().toString()}
removeClippedSubviews={true}
maxToRenderPerBatch={2}
ListFooterComponent={Loader}
onEndReachedThreshold={0.5}
onEndReached={() => {
loadData(true);
}}
/>
</View>
);
}
class PostCard extends React.PureComponent {
onPressHandler() {
console.log("Clicked");
alert("Clicked");
}
render() {
let image = <></>
if (this.props.jetpack_featured_media_url.trim() !== "") {
image = <Image style={{ flex: 1 }} source={{
//uri: this.props.featuredimage,
uri: this.props.jetpack_featured_media_url,
}} />
}
// console.log(this.props.jetpack_featured_media_url);
return <TouchableNativeFeedback onPress={()=>{
this.onPressHandler();
}}>
<View style={{ margin: 10 }}>
<Text style={{ fontSize: 17, lineHeight: 23, fontWeight: "600" }}>{ this.props.title.rendered}</Text>
</View></TouchableNativeFeedback>
}
}
Try to import 'TouchableNativeFeedback' from 'react-native' instead of 'react-native-gesture-handler'.
Related
My file name is talesrunner23 and splash.png file is in ASSETS, but when you run it, it says it can't be found
<Image source={require('/talesrunner23/assets/splash.png/')}They say I'm wrong here
please help me I'm a beginner, so I have to write down all the codes
please please
I don't know at all
*import React, { useRef, useState, useCallback, useEffect } from 'react';
import {
View,
BackHandler,
Platform,
StyleSheet,
ActivityIndicator,
} from 'react-native';
import { WebView } from 'react-native-webview';
const DELAY_BEFORE_WEBVIEW = 10; // <--- seconds before webview load
export default function App() {
// ref
const webView = useRef();
// callbacks
const handleBack = useCallback(() => {
if (canGoBack && webView.current) {
webView.current.goBack();
return true;
}
return false;
`enter code here`;
}, [canGoBack]);
// effects
useEffect(() => {
BackHandler.addEventListener('hardwareBackPress', handleBack);
return () => {
BackHandler.removeEventListener('hardwareBackPress', handleBack);
};
}, [handleBack]);
useEffect(() => {
setTimeout(() => {
setIsLoading(false);
}, 30 * DELAY_BEFORE_WEBVIEW);
}, []);
// states
const [canGoBack, setCanGoBack] = useState(false);
const [isLoading, setIsLoading] = useState(true);
return (
<View style={styles.container}>
<WebView
ref={webView}
source={{ uri: 'https://www.talesrunnerbestguild.co.kr/' }}
style={styles.webView}
onLoadProgress={(event) => setCanGoBack(event.nativeEvent.canGoBack)}
/>
{isLoading && <CenterLoader />}
</View>
);
}
const CenterLoader = () => (
<View style={styles.loaderContainer}>
<Image source={require('/talesrunner23/assets/splash.png/')}
style={{height:100,width:100}}/>
</View>
);
const styles = StyleSheet.create({
container: { flex: 1 },
loaderContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
position: 'absolute',
width: '100%',
height: '100%',
backgroundColor:'white' // <-- comment this to show webview while loading
},
webView:
Platform.OS === 'ios'
? { marginTop: 30, marginBottom: 40 }
: { marginTop: 30 },
});
You will not want to require but import the Image source at the top. Here is a quick example I made in a sandbox: https://codesandbox.io/s/image-example-c4irqo?file=/src/App.js
import Cat from "./cat.jpeg";
function App() {
return (
<View style={styles.app}>
<View style={styles.header}>
<Image
accessibilityLabel="Cat"
source={Cat}
resizeMode="contain"
style={styles.logo}
/>
<Text style={styles.title}>Image Example</Text>
</View>
</View>
);
}
I am new to react native. I am writing the code in class component, I need a flatlist with checkbox that allow multiple select, add the selected data could be save in a array and pass to other screen. I have no idea about that.. Can anyone help??
Try this example:
import React, { Component } from 'react';
import { View, Text, Image, FlatList, TextInput } from 'react-native';
import { ListItem, Body } from 'native-base';
import CheckBox from '#react-native-community/checkbox';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
serachdata: [],
selectedId: []
};
}
componentDidMount(){
this.ApiCalling();
}
ApiCalling = () => {
const url = "YOUR_API"
fetch(url)
.then(res => res.json())
.then(res => {
this.setState({data: res.data})
this.setState({serachdata: res.data})
})
.catch(error => {
console.log("error ", error)
})
}
onCheckBoxPress = (id) => {
let checkboxes = this.state.selectedId;
if(checkboxes && checkboxes.includes(id)){
checkboxes.splice( checkboxes.indexOf(id), 1 );
} else {
checkboxes = checkboxes.concat(id);
}
this.setState({selectedId:checkboxes})
console.log("selectde checkbox id: ", checkboxes)
}
_filter = (text) => {
const newData = this.state.data.filter(item => {
const itemData = `${item.first_name.toUpperCase()}`;
const textData = text.toUpperCase();
return itemData.indexOf(textData) > -1;
});
this.setState({serachdata:newData})
}
render(){
return(
<View style={{ marginTop: 50 }}>
<TextInput
autoCapitalize='none'
autoCorrect={false}
style={{ height: 40, borderColor: 'gray', borderWidth: 1, margin:10 }}
status='info'
placeholder='Search'
onChangeText={(text) => this._filter(text)}
textStyle={{ color: '#000' }}
/>
<FlatList
data={this.state.serachdata}
keyExtractor={(item, index) => String(index)}
renderItem={({item, index}) => {
return(
<ListItem key={index}>
<CheckBox
disabled={false}
value={this.state.selectedId && this.state.selectedId.includes(item.id)}
onChange={()=>this.onCheckBoxPress(item.id)}
/>
<Body>
<View style={{flexDirection:'row', justifyContent:'space-between'}}>
<Image source={{ uri: item.avatar }} style={{ height: 100, width: 100 }} />
<Text>{item.first_name}</Text>
<Text>{item.email}</Text>
{/* <Text>{item.name.last}</Text> */}
</View>
</Body>
</ListItem>)}}
/>
</View>
)
}
};
I am developing React Native app, where must be overlay view before starting. Like "welcome screen". After you have read content in overlay view, you press ok.
setState is not working. Throw error TypeError: _this.setState is not a function
I'll tried to use setState like this:
removeElement = () => {
//this.state.loadingScreen = false;
this.setState({
loadingScreen: false
})
console.log(`Staten arvo nyt: ` + this.state.loadingScreen);
When I use setState it should re-render component, but I don't know what is the reason why it not works. I can change state this.state.loadingScreen = false;, but it is not re-rendering. And forceUpdate() not working either.
QUESTION: How can I render component again to get rid off overlay view?
My code:
import React, { useState, setState, Component } from "react";
import { Text, View, StyleSheet, TextInput, Button, Alert, TouchableOpacity} from "react-native";
import AsyncStorage from '#react-native-community/async-storage';
export default function StartPage({ navigation }) {
state = {
loadingScreen: true,
}
let userInput = "";
let userInputName = "";
readUserInput = (text) => {
userInput = text
}
readUserInputName = (text) => {
userInputName = text
}
checkUserInput = () => {
if(userInput.length < 1 && userInputName.length < 1){
Alert.alert("Tarkista rekisterinumero ja nimi")
}
else{
storeData = async () => {
try{
AsyncStorage.setItem("RegistrationNumber", userInput);
AsyncStorage.setItem("UserName", userInputName);
}
catch(e){
console.log(e);
}
}
storeData();
navigation.navigate("GeneralInspection")
}
}
renderElement = () =>{
if(this.state.loadingScreen == true)
return <View style={styles.fullScreen}>
<TouchableOpacity style={styles.button} onPress={this.removeElement}>
<Text style={{fontSize:20}}>Change state to false</Text>
</TouchableOpacity>
</View>;;
return null;
}
removeElement = () => {
this.state.loadingScreen = false
}
setTimeout(
function() {
}
.bind(this),
1000
);
return (
<View style={styles.view}>
{ this.renderElement() }
<Text style={styles.text}>Tiedot</Text>
<TextInput
style={styles.inputStyle}
placeholder="Nimi"
onChangeText={this.readUserInputName}
autoCapitalize="words"
/>
<TextInput
style={styles.inputStyle}
placeholder="ABC-123"
onChangeText={this.readUserInput}
autoCapitalize="characters"
/>
<TouchableOpacity style={styles.button} onPress={this.checkUserInput}>
<Text style={{fontSize:20}}>Aloita</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
view: {
height: "100%",
width: "100%",
justifyContent: "center",
alignItems: "center",
},
text: {
fontSize: 40,
fontWeight: "bold",
padding:10,
fontStyle:"italic"
},
inputStyle: {
height:50,
borderColor:"black",
borderWidth:1,
width:"50%",
marginBottom:15,
textAlign:"center",
fontSize: 20,
borderRadius:5,
},
button: {
backgroundColor:"#007bff",
borderRadius:5,
padding:8,
width:"50%",
height: 60,
alignItems:"center",
justifyContent: "center",
},
fullScreen: {
height: "100%",
width: "100%",
zIndex: 100,
position: "absolute",
backgroundColor: "red",
}
});
You are using a functional component in the OP, which requires you to use useState Hooks for managing your state.
If you were using a class component, you could use the setState approach. In both cases, React will take care of the re-rendering part.
If using class-based approach, mutating state like this.state.loadingScreen = false will not trigger a re-render of the UI. You will have to use the setState method instead.
Class-based approach
import React from "react";
import { Text, View, StyleSheet, TextInput, Alert, TouchableOpacity } from "react-native";
import AsyncStorage from '#react-native-community/async-storage';
export default class StartPage extends React.Component {
constructor(props) {
super(props);
this.state = {
loadingScreen: true,
userInput: "",
userInputName: ""
};
}
readUserInput = (text) => {
this.setState({
userInput: text
});
}
readUserInputName = (text) => {
this.setState({
userInputName: text
});
}
storeData = async () => {
const { userInput, userInputName } = this.state;
try {
AsyncStorage.setItem("RegistrationNumber", userInput);
AsyncStorage.setItem("UserName", userInputName);
this.props.navigation.navigate("GeneralInspection");
}
catch (e) {
console.log(e);
}
}
checkUserInput = () => {
const { userInput, userInputName } = this.state;
if (userInput.length < 1 && userInputName.length < 1)
Alert.alert("Tarkista rekisterinumero ja nimi");
else
this.storeData();
}
renderElement = () => {
if (this.state.loadingScreen)
return (
<View style={styles.fullScreen}>
<TouchableOpacity style={styles.button} onPress={this.removeElement}>
<Text style={{ fontSize: 20 }}>Change state to false</Text>
</TouchableOpacity>
</View>
);
return null;
}
removeElement = () => {
this.setState({
loadingScreen: false
});
}
render() {
return (
<View style={styles.view} >
{ this.renderElement()}
< Text style={styles.text} > Tiedot</Text>
<TextInput
style={styles.inputStyle}
placeholder="Nimi"
value={this.state.userInputName}
onChangeText={this.readUserInputName}
autoCapitalize="words"
/>
<TextInput
style={styles.inputStyle}
placeholder="ABC-123"
value={this.state.userInput}
onChangeText={this.readUserInput}
autoCapitalize="characters"
/>
<TouchableOpacity style={styles.button} onPress={this.checkUserInput}>
<Text style={{ fontSize: 20 }}>Aloita</Text>
</TouchableOpacity>
</View >
);
}
}
Functional component-based approach
import React, { useState } from "react";
import { Text, View, StyleSheet, TextInput, Alert, TouchableOpacity } from "react-native";
import AsyncStorage from '#react-native-community/async-storage';
export default function StartPage({ navigation }) {
const [loadingScreen, setLoadingScreen] = useState(true);
const [userInput, setUserInput] = useState('');
const [userInputName, setUserInputName] = useState('');
const readUserInput = (text) => {
setUserInput(text);
};
const readUserInputName = (text) => {
setUserInputName(text);
};
const storeData = async () => {
try {
AsyncStorage.setItem("RegistrationNumber", userInput);
AsyncStorage.setItem("UserName", userInputName);
navigation.navigate("GeneralInspection");
}
catch (e) {
console.log(e);
}
};
const checkUserInput = () => {
if (userInput.length < 1 && userInputName.length < 1)
Alert.alert("Tarkista rekisterinumero ja nimi");
else
storeData();
};
const renderElement = () => {
if (loadingScreen)
return (
<View style={styles.fullScreen}>
<TouchableOpacity style={styles.button} onPress={removeElement}>
<Text style={{ fontSize: 20 }}>Change state to false</Text>
</TouchableOpacity>
</View>
);
return null;
};
const removeElement = () => {
setLoadingScreen(false);
};
return (
<View style={styles.view} >
{renderElement()}
< Text style={styles.text} > Tiedot</Text>
<TextInput
style={styles.inputStyle}
placeholder="Nimi"
value={userInputName}
onChangeText={readUserInputName}
autoCapitalize="words"
/>
<TextInput
style={styles.inputStyle}
placeholder="ABC-123"
value={userInput}
onChangeText={readUserInput}
autoCapitalize="characters"
/>
<TouchableOpacity style={styles.button} onPress={checkUserInput}>
<Text style={{ fontSize: 20 }}>Aloita</Text>
</TouchableOpacity>
</View >
);
}
Now I would like to pull down a scrollView and refresh this component, so I have followed the official document mentioning onRefresh and RefreshContorol from react-native.
However, I don't know why my code is not working and get error...
The code below is my code.
<View style={styles.container}>
<ScrollView
contentContainerStyle={{ flexDirection: 'row', flexWrap: 'wrap' }}
refreshControl={<RefreshControl refreshing={this.state.refreshing} onRefresh={this.setState({ refreshing: true })} />}
>
{this.renderItemBox()}
</ScrollView>
</View>
Below is the sample code in which you can find RefreshController integration with ScrollView:
import { ScrollView, RefreshControl } from 'react-native';
class RefreshableList extends Component {
constructor(props) {
super(props);
this.state = {
refreshing: false,
};
}
_onRefresh = () => {
this.setState({refreshing: true});
fetchData().then(() => {
this.setState({refreshing: false});
});
}
render() {
return (
<ScrollView
refreshControl={
<RefreshControl
refreshing={this.state.refreshing}
onRefresh={this._onRefresh}
/>
}
/>
);
}
}
on me it worked
import React from 'react';
import {
RefreshControl,
ScrollView,
StyleSheet,
View,
} from 'react-native';
import {WebView} from 'react-native-webview';
const wait = timeout => {
return new Promise(resolve => setTimeout(resolve, timeout));
};
const Main = () => {
const webViewRef = React.useRef();
const [refreshing, setRefreshing] = React.useState(false);
const [enablePTR, setEnablePTR] = React.useState(false);
const onRefresh = React.useCallback(() => {
webViewRef.current.reload();
setRefreshing(true);
wait(2000).then(() => setRefreshing(false));
}, []);
const handleEvent = e => {
if (e.nativeEvent.contentOffset.y > 100) {
setEnablePTR(false);
} else {
setEnablePTR(true);
}
};
return (
<View style={styles.container}>
<ScrollView
scrollEnabled={false}
contentContainerStyle={styles.container}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={onRefresh}
enabled={enablePTR}
/>
}
>
<WebView
source={{ uri: "https://example.com/" }}
ref={(ref) => (webViewRef.current = ref)}
javaScriptEnabled={true}
injectedJavaScript="setTimeout(() => {document.addEventListener('scroll', function (event) {window.ReactNativeWebView.postMessage(JSON.stringify(document.getElementsByClassName('topconteiner')[0].scrollTop));},true);}, 300);true;"
onScroll={(event) => handleEvent(event)}
/>
</ScrollView>
</View>
);
};
export default Main;
const styles = StyleSheet.create({
container: {backgroundColor: '#fff', flex: 1},
});
My situation is i have two FlatList component (A and B).
A is my first screen. I use react-navigation to navigate A to B , B will show back arrow on headerLeft. When i click the arrow it will back to A . But the FlatList data is still show B even it is really in A...
My data is from fetch API by react-redux, i think the problem is come from react-redux. Because i test a simple test without react-redux. The problem is gone.
I want to use react-redux create my project. I try to use shouldComponentUpdate like
shouldComponentUpdate = (nextProps, nextState) => {
if (nextProps.movieList === this.props.movieList) {
return false;
}
return true;
};
It is still can't fix my problem when goBack() to another component
I console.log it try to find what is going on with my props data.
When i navigate to B from A. My console.log will show like this, i find A component will be rendered...
Then i click the back arrow on headerLeft to A. The screen is A but the data is still B add my console.log is empty at the same time.
I can't figure it out. Any help would be appreciated. Thanks in advance.
Here is my A component file (B is similar with A):
import React, { Component } from 'react';
import {
View, FlatList, Dimensions,
TouchableOpacity, Image,
ActivityIndicator, Alert, Platform
} from 'react-native';
import { Icon } from 'react-native-elements';
import { connect } from 'react-redux';
import { fetchMainMovieList } from '../actions';
const { width, height } = Dimensions.get('window');
const equalWidth = (width / 2);
class MainActivity extends Component {
static navigationOptions = ({ navigation }) => ({
title: 'MainActivity',
headerLeft:
<TouchableOpacity style={{ marginLeft: 10 }} onPress={() => navigation.navigate('DrawerOpen')} >
<Icon name='menu' />
</TouchableOpacity>
});
componentWillMount() {
this.props.fetchMainMovieList();
}
renderItem({ item }) {
return (
<View>
<Image
source={{ uri: item.photoHref }}
style={{ height: 220, width: equalWidth }}
resizeMode="cover"
/>
</View>
);
}
render() {
const movieData = this.props.movieList.movie;
console.log('A component this.props=>');
console.log(this.props);
if (movieData === []) {
return (
<View style={styles.loadingStyle}>
<ActivityIndicator />
</View>
);
}
return (
<View style={{ flex: 1 }}>
<FlatList
data={movieData}
renderItem={this.renderItem}
numColumns={2}
horizontal={false}
keyExtractor={(item, index) => index}
/>
</View>
);
}
}
const styles = {
loadingStyle: {
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center'
}
};
const mapStateToProps = (state) => {
const movieList = state.movieList;
return { movieList };
};
export default connect(mapStateToProps, { fetchMainMovieList })(MainActivity);
Here is my B component file:
import React, { Component } from 'react';
import {
View, FlatList, Dimensions,
Image, ActivityIndicator, Text
} from 'react-native';
import { connect } from 'react-redux';
import { fetchThisWeek } from '../actions';
const { width, height } = Dimensions.get('window');
const equalWidth = (width / 2);
class ThisWeek extends Component {
static navigationOptions = ({ navigation }) => ({
title: 'ThisWeek',
});
componentWillMount() {
this.props.fetchThisWeek();
}
renderItem({ item }) {
return (
<View>
<Image
source={{ uri: item.photoHref }}
style={{ height: 500, width: '100%' }}
resizeMode="cover"
/>
</View>
);
}
render() {
const movieData = this.props.movieList.movie;
console.log('B component this.props=>');
console.log(this.props);
if (movieData === []) {
return (
<View style={styles.loadingStyle}>
<ActivityIndicator />
</View>
);
}
return (
<View style={{ flex: 1 }}>
<FlatList
data={movieData}
renderItem={this.renderItem}
numColumns={1}
horizontal={false}
keyExtractor={(item, index) => index}
/>
</View>
);
}
}
const styles = {
loadingStyle: {
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center'
}
};
const mapStateToProps = (state) => {
const movieList = state.movieList;
return { movieList };
};
export default connect(mapStateToProps, { fetchThisWeek })(ThisWeek);
Here is my MyListReducer.js:
import {
MOVIELIST_MAINACTIVITY,
MOVIELIST_THISWEEK,
MOVIELIST_THEATER
} from '../actions/types';
const INITIAL_STATE = {};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case MOVIELIST_MAINACTIVITY:
return action.payload;
case MOVIELIST_THISWEEK:
return action.payload;
case MOVIELIST_THEATER:
console.log(action.payload);
return action.payload;
default:
return state;
}
};
In your reducer you have added the fetched data into the main object in store, instead, you should have to maintain two different variables to save data of those different components separately. Try by changing the reducer as,
import {
MOVIELIST_MAINACTIVITY,
MOVIELIST_THISWEEK,
MOVIELIST_THEATER
} from '../actions/types';
const INITIAL_STATE = {
weeklyMovies:[],
allMovies:[]
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case MOVIELIST_MAINACTIVITY:
return {
...state,
allMovies:action.payload
};
case MOVIELIST_THISWEEK:
return {
...state,
weeklyMovies:action.payload
};
case MOVIELIST_THEATER:
console.log(action.payload);
return action.payload;
default:
return {...state};
}
};
And in your component A and B you should change your mapStateToProps to read data from corresponding objects in store.
For MainActivity component
const mapStateToProps = (state) => {
const movieList = state.allMovies;
return { movieList };
};
and for ThisWeek component
const mapStateToProps = (state) => {
const movieList = state.weeklyMovies;
return { movieList };
};