How to make independent style change on React mapped component - reactjs

I have problem when I try to change the style of a mapped TouchableOpacity.
I map a list of TouchableOpacity and I would like that when I click on one, the backgroundColor change (in black) only on the on I clicked but also to reset the backgroundColor of the other TouchableOpacity I clicked before.
So for example, if I click on the first TouchableOpacity the background of that one become black. And after, if I click on the second, the background of the second become black but the background of the first become again grey.
export default class Playground extends Component {
state = {
isPressed: false
};
handlePress = () => {
this.setState({
isPressed: !this.state.isPressed
});
};
render() {
let array = [0, 1, 2];
return (
<View style={styles.container}>
<Text>Test</Text>
{array.map(item => {
return (
<TouchableOpacity
key={item}
style={this.state.isPressed ? styles.buttonPressed : styles.button}
onPress={this.handlePress}
>
<Text>Click on it</Text>
</TouchableOpacity>
);
})}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
marginTop: 50
},
button: {
backgroundColor: 'grey'
},
buttonPressed: {
backgroundColor: 'black'
}
});
This is what I tried but when I click on one TouchableOpacity the backgroundColor of all of them change.
I would like to target only one and reset the other in the same time

it's good working
[import React, { Component } from 'react';
import { StyleSheet, Text, View, TouchableOpacity } from 'react-native';
export default class Playground extends Component {
state = {
isPressed: false
};
handlePress = () => {
this.setState({
isPressed: !this.state.isPressed
});
};
render() {
let array = \[0, 1, 2\];
return (
<View style={styles.container}>
<Text>Test</Text>
{array.map(item => {
return (
<TouchableOpacity
key={item}
style={this.state.isPressed === false ? styles.buttonPressed : styles.button}
onPress={this.handlePress}
>
<Text>Click on it</Text>
</TouchableOpacity>
);
})}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
marginTop: 50
},
button: {
backgroundColor: 'grey'
},
buttonPressed: {
backgroundColor: 'black'
}
});

Not sure if it's the best way to resolve the problem, but I find a potential solution using direct manipulation
I start by assigning a different reference to each TouchableOpacity and after I target that ref and use setNativeProps to change the style of the target
export default class Playground extends Component {
state = {
daySelected: null
}
handlePress = (day, i) => {
if (this.state.daySelected !== null) {
this.state.daySelected.setNativeProps({
backgroundColor: 'grey'
});
}
this.setState({
daySelected: day
});
day.setNativeProps({
backgroundColor: 'black'
});
};
render() {
let array = [0, 1, 2];
return (
<View style={styles.container}>
<Text>Test</Text>
{array.map(i => {
return (
<TouchableOpacity
key={i}
ref={thisItem => (this[`item-${i}`] = thisItem)}
style={styles.button}
onPress={() => this.handlePress(this[`item-${i}`], i)}
>
<Text>Click on it</Text>
</TouchableOpacity>
);
})}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
marginTop: 50
},
button: {
backgroundColor: 'grey'
}
});

Related

react-native-pdf cannot set currentPage state to show up on display

i've done the react-native-pdf to show the slides of my pdf file. But, I want to set the current page state to show up on the display. And when the value is set, it'll refresh that screen and goes back to the first page automatically.
this is my code:
import React, { useState } from 'react';
import { StyleSheet, Dimensions, View, Text } from 'react-native';
import Pdf from 'react-native-pdf';
function Work() {
const [currentPage, setCurrentPage] = useState()
const ShowPdf = () => {
const source = { uri: 'http://samples.leanpub.com/thereactnativebook-sample.pdf', cache: true };
return (
<Pdf
source={source}
onLoadComplete={(numberOfPages, filePath) => {
console.log(`number of pages: ${numberOfPages}`);
}}
onPageChanged={(page, numberOfPages) => {
console.log(`current page: ${page}`);
setCurrentPage(page) //set the cuurentPage
}}
onError={(error) => {
console.log(error);
}}
onPressLink={(uri) => {
console.log(`Link presse: ${uri}`)
}}
style={styles.pdf} />
)
}
return (
<View style={styles.container}>
<ShowPdf />
<View style={styles.pageNumber}>
<Text>{currentPage}</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'flex-start',
alignItems: 'center',
//marginTop: 25,
//backgroundColor: 'red',
backfaceVisibility: 'hidden'
},
pdf: {
flex: 1,
width: Dimensions.get('window').width,
height: Dimensions.get('window').height,
},
pageNumber: {
position: 'absolute',
left: 20,
top: 20,
backgroundColor: 'rgba(173, 173, 173, 0.5)',
padding: 13,
borderRadius: 6,
}
});
export default Work;
my emulator display:
image
Anyway I can fix this?
Instead of use this
<View style={styles.container}>
<ShowPdf />
<View style={styles.pageNumber}>
<Text>{currentPage}</Text>
</View>
</View>
please use it
<View style={styles.container}>
<Pdf
source={{ uri: path }}
onLoadComplete={(numberOfPages, filePath) => {
console.log(`number of pages: ${numberOfPages}`);
}}
onPageChanged={(page, numberOfPages) => {
console.log(`current page: ${page}`);
setCurrentPage(page) //set the cuurentPage
}}
onError={(error) => {
console.log(error);
}}
onPressLink={(uri) => {
console.log(`Link presse: ${uri}`)
}}
style={styles.pdf} />
<View style={styles.pageNumber}>
<Text>{currentPage}</Text>
</View>
</View>
<ShowPdf/> is your custom react component it will be re-rendered for every page change. So, that is why you faced this problem

ReferenceError: FlatListItemSeparator is not defined in React Native

I'm new to React Native, and I'm following along with an online tutorial.
This is my App.js file:
import React, { useState, useEffect } from 'react';
import {View,Text,ImageBackground,FlatList, Image, TouchableHighlight } from 'react-native';
import bookmarkIcon from './assets/bookmark.png';
import readIcon from './assets/read.png';
import styles from './styles';
const ArticleItem = ({article}) => {
const { title, description, url, urlToImage } = article;
return (
<View style = { styles.articleContainer }>
<Image style={ styles.articleImage } source={{ uri: urlToImage }} />
<Text style= { styles.articleTitle }>
{ title }
</Text>
<Text style = { styles.articleDescription }>
{ description }
</Text>
<View style = { styles.articleBtns}>
<IconButton width= "50%" color = "white" bgcolor = "#ff5c5c" icon = { readIcon } onPress = { () => { console.log("Button pressed!")} } title = "Open" />
<IconButton width= "50%" color = "white" bgcolor = "#ff5c5c" icon = { bookmarkIcon } onPress = { () => { console.log("Button pressed!")} } title = "Read later" />
</View>
</View>
)
}
FlatListItemSeparator = () => {
return (
<View
style={{
height: 1,
width: "100%",
backgroundColor: "#000",
}}
/>
);
}
FlatListHeader = () => {
return (
<View elevation={1}
style={{
height: 100,
width: "97%",
margin: 5,
backgroundColor: "#fff",
border: 2.9,
borderColor: "black",
alignSelf: "center",
shadowColor: "#000",
shadowOffset: {
width: 0,
height: 16,
},
shadowOpacity: 1,
shadowRadius: 7.49
}}
>
<Text style={{
textShadowColor: 'black',
textShadowOffset: { width: 1, height: 3 },
textShadowRadius: 10,
fontSize: 40,
fontWeight: '800',
flex: 1,
alignSelf: "center",
paddingTop: 30
}}
>Latest articles</Text>
</View>
);
}
const IconButton = ({title, color, bgcolor, onPress, width, icon }) =>{
return (
<TouchableHighlight onPress = { onPress } style= { { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', backgroundColor: bgcolor } }>
<View style={ {width: width, flexDirection: 'row', justifyContent: 'center', alignItems: 'center' } }>
<Image style = { { height: 27, width:27, margin : 5 } } source = { icon }></Image>
<Text style = { {color: color }} > { title } </Text>
</View>
</TouchableHighlight>
);
}
const HomeScreen = (props) => {
console.log("articles: ", props.articles);
return (
<View>
<FlatList
data={ props.articles }
ListHeaderComponent = { this.FlatListHeader }
ItemSeparatorComponent = { this.FlatListItemSeparator}
keyExtractor={(item, index) => index.toString()}
renderItem={({item}) => <ArticleItem article = { item } />}
/>
</View>
);
}
const SplashScreen = (props) => {
return (
<View style = { styles.container } >
<ImageBackground style= { styles.backgroundImage } source={{uri: 'http://i.imgur.com/IGlBYaC.jpg'}} >
<View style= { styles.logoContainer }>
<Text style = { styles.logoText }>
Newzzz
</Text>
<Text style = { styles.logoDescription }>
Get your doze of daily news!
</Text>
</View>
</ImageBackground>
</View>
);
}
const App = () => {
const URL = 'https://raw.githubusercontent.com/nimramubashir/React-Native/fetch/articles.json';
const [articles, setArticles] = useState([]);
const [loading, setLoading ] = useState(true);
useEffect(()=>{
fetch(URL)
.then((response) => response.json())
.then((responseJson) => {
return responseJson.articles;
})
.then( articles => {
setArticles(articles);
console.log(articles);
setLoading(false);
})
.catch( error => {
console.error(error);
});
} , []);
if (loading){
return <SplashScreen />
} else {
return <HomeScreen articles = { articles }/>
}
};
export default App
The code is the same as the tutorial, but when I'm trying to run this code, I'm getting an Error
ReferenceError: FlatListItemSeparator is not defined
I've tried to import the FlatListItemSeparator, but since it is read-only, I can't. I'm getting this error at both FlatListItemSeparator and FlatListHeader. Why am I getting this error, and how can I solve it?
you are having each component as separate Function-Component. The tutorial is probably based on Class-Components. In Function-Components there is no this, so just remove the this.

Make the Header scroll down with FlatList and Animated - React Native

I didn't find any soulotion to do animated with FlatList,
I want to hide my Header when I scroll down like In Facebook app.
I tried To use FlatList with diffClamp() without success,
I don't know if I can do it with FlatList but I need also LazyLoading,
Someone Can help me?
This is my Header:
import React, { useState } from "react";
import {
View,
Animated,
Text,
Dimensions,
TouchableOpacity
} from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import { Ionicons } from "#expo/vector-icons";
const Header = props => {
const params = props.scene.route.params;
const [headerHeight] = useState(
params !== undefined && params.changingHeight !== undefined
? params.changingHeight
: Dimensions.get("window").height * 0.065
);
return (
<SafeAreaView style={{ backgroundColor: "rgb(152,53,349)" }}>
<Animated.View
style={{
width: Dimensions.get("window").width,
height: headerHeight,
flexDirection: "row",
backgroundColor: "rgb(152,53,349)"
}}
>
<TouchableOpacity
onPress={() => {
props.navigation.openDrawer();
}}
>
<View
style={{
paddingVertical: "15%",
justifyContent: "center",
paddingHorizontal: 25
}}
>
<Ionicons name="ios-menu" size={30} color={"white"} />
</View>
</TouchableOpacity>
<View style={{ justifyContent: "center", marginLeft: "23%" }}>
<Text
style={{
fontSize: 20,
fontWeight: "bold",
textAlign: "center",
color: "white"
}}
>
MyGameenter code here{" "}
</Text>
</View>
</Animated.View>
</SafeAreaView>
);
};
export default Header;
This is my FlatLIst:
import React from "react";
import { View, FlatList, StyleSheet } from "react-native";
import { EVENTS } from "../data/dummy-data";
import Event from "./Event";
const renderGridItem = itemData => {
return <Event item={itemData.item} />;
};
const ShowEvents = props => {
return (
<View style={styles.list}>
<FlatList
keyExtractor={(item, index) => item.id}
data={EVENTS}
renderItem={renderGridItem}
numColumns={1}
/>
</View>
);
};
const styles = StyleSheet.create({
list: {
flex: 1,
justifyContent: "center",
alignItems: "center"
}
});
export default ShowEvents;
Use
onScroll={(e) => console.log(e.nativeEvent.contentOffset.y)}
Working Example: https://snack.expo.io/#msbot01/privileged-candies
import React, { Component } from 'react';
import { Text, View, StyleSheet, ScrollView, FlatList } from 'react-native';
import Constants from 'expo-constants';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
DATA: [],
previous: 0,
hide: false,
};
}
componentDidMount() {
var array = [];
for (var i = 0; i < 100; i++) {
var a = { id: i, value: i };
array.push(a);
}
this.setData(array);
}
setData(a) {
this.setState({
DATA: a,
});
}
Item({ title }) {
return (
<View
style={{
width: '100%',
height: 30,
marginTop: 5,
backgroundColor: 'green',
justifyContent: 'center',
alignItems: 'center',
}}>
<Text />
</View>
);
}
_onScroll(event) {
// console.log('>>>>>>>>>>>'+this.state.data);
if (this.state.previous < event) {
this.setState({
hide: true,
previous: event,
});
} else {
this.setState({
hide: false,
previous: event,
});
}
console.log(event);
}
render() {
return (
<View style={{ flex: 1 }}>
{this.state.hide == true ? null : (
<View
style={{
width: '100%',
height: 50,
justifyContent: 'center',
alignItems: 'center',
}}>
<Text>Hide me while scrolling</Text>
</View>
)}
<FlatList
onScroll={e => this._onScroll(e.nativeEvent.contentOffset.y)}
data={this.state.DATA}
renderItem={({ item }) => (
<View
style={{
width: '100%',
height: 30,
marginTop: 5,
backgroundColor: 'green',
justifyContent: 'center',
alignItems: 'center',
}}>
<Text />
</View>
)}
keyExtractor={item => item.id}
/>
</View>
);
}
}
const styles = StyleSheet.create({});
import React, { useState } from "react";
import { View, FlatList, StyleSheet, Platform, Dimensions } from "react-native";
import { EVENTS } from "../data/dummy-data";
import Event from "./Event";
const HeaderHeight = () => {
if (Platform.OS === "android" && Dimensions.get("window").height < 600)
return Dimensions.get("window").height * 0.075 + 20;
else if (Platform.OS === "android")
return Dimensions.get("window").height * 0.058 + 20;
else return Dimensions.get("window").height * 0.01 + 20;
};
const renderGridItem = itemData => {
return <Event item={itemData.item} />;
};
const ShowEvents = props => {
const [previous, SetPrevious] = useState(false);
const [hide, SetHide] = useState(false);
_onScroll = event => {
const headerHeight = HeaderHeight();
if (event > headerHeight) {
SetHide(true);
props.navigation.setParams({
hide: true
});
SetPrevious(event);
} else if (event < 0.1) {
SetHide(false);
props.navigation.setParams({
hide: false
});
SetPrevious(event);
}
};
return (
<View style={styles.list}>
<FlatList
onScroll={e => _onScroll(e.nativeEvent.contentOffset.y)}
keyExtractor={(item, index) => item.id}
data={EVENTS}
renderItem={renderGridItem}
numColumns={1}
/>
</View>
);
};
const styles = StyleSheet.create({
list: {
flex: 1,
justifyContent: "center",
alignItems: "center"
}
});
export default ShowEvents;

ScrollView not enabling scrolling in React Native App

Using Expo and the Android App, the <ScrollView> section is not scrolling. I've read through a lot of related questions but can't seem to nail down the issue. Can anyone shed some light for a react native newbie?
On pressing the button the API is requested and then the article headlines are rendered. This all works great but the headlines are not scrollable and don't all fit on the screen. Am I misunderstanding what <ScrollView> does?
import React from 'react'
import { ScrollView, StyleSheet, Text, View, Footer } from 'react-native'
import { Font } from 'expo'
import { Button } from 'react-native-elements'
export default class App extends React.Component {
state = {
fontLoaded: true,
buttonPressed: false,
showButton: true
}
onPress = (e) => {
e.preventDefault()
this.fetchArticlesAsync()
this.setState({
buttonPressed: true,
showButton: false
})
}
async fetchArticlesAsync() {
await fetch('http://content.guardianapis.com/search?show-fields=all&api-key=d8d8c012-6484-4bb4-82d7-2770a7c5d029')
.then(response => response.json())
.then(responseJSON => {
return this.setState({
articleList: responseJSON.response.results
})
})
.catch((error) => {
console.log(error)
})
}
render() {
return (
<ScrollView contentContainerStyle={styles.container}>
{
this.state.fontLoaded &&
<View style={this.state.buttonPressed ? styles.newsContainer : styles.container}>
<Text style={styles.header}>Newsli</Text>
{this.state.showButton &&
<Button buttonStyle={styles.button} onPress={this.onPress} clear text='Feed Me' />}
{this.state.buttonPressed &&
<View>
{this.state.articleList ?
<View style={styles.articleContainer}>
{this.state.articleList.map((article, i) => {
return <View style={styles.articleContainer} key={i}>
<Text style={styles.text}>{article.webTitle}</Text>
</View>
})}
</View> : <Text style={styles.loading}>Loading</Text>}
</View>}
</View>
}
</ScrollView>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#000',
alignItems: 'center',
justifyContent: 'center',
},
newsContainer: {
flex: 1,
backgroundColor: '#000',
alignItems: 'center',
justifyContent: 'flex-start',
marginTop: 15
},
articleContainer: {
flex: 1,
backgroundColor: '#000',
alignItems: 'flex-start',
justifyContent: 'flex-start',
marginTop: 15
},
header: {
fontSize: 90,
color: '#fff'
},
text: {
fontSize: 20,
color: '#fff',
},
loading: {
fontSize: 24,
color: '#FF69B4'
},
button: {
borderColor: '#fff',
borderWidth: 2
}
})
It was quite small mistake. The ScrollView does not know current device's screen size. You need to have ScrollView inside a View component. Try the following...
<View style={styles.container}>
<ScrollView>
{...}
</ScrollView>
</View>
Next time when you submit a question strip out all nonsense code. Like Font fetching.
Good luck!

Avoid view to be refreshed in a tab based app in react Native

I have a simple tabbar app.
One of the tabs renders a view where I display pictures fetched remotely.
The issue I'm facing is, whenever I switch tabs, the images are re-displayed, despite the fact than neider render is called again, nor shouldComponentUpdate.
This results in a very bad user experience as the view is refreshed over and over again.
Any idea of how I can fix it?
/**
* Sample React Native App
* https://github.com/facebook/react-native
* #flow
*/
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
TabBarIOS,
ScrollView,
Image,
Dimensions
} from 'react-native';
var {height, width} = Dimensions.get('window');
class TabIssue extends Component {
constructor(props){
super(props);
this.state = {
selectedTab: 'tab1',
items: []
}
}
componentDidMount() {
// Build an array of 60 photos
let items = Array.apply(null, Array(60)).map((v, i) => {
return { id: i, src: 'http://placehold.it/200x200?text='+(i+1) }
});
this.setState({ items });
}
render() {
return (
<TabBarIOS>
<TabBarIOS.Item title="Tab #1"
selected={this.state.selectedTab === 'tab1'}
onPress={() => {
this.setState({ selectedTab: 'tab1', });
}}>
<View style={{marginTop: 20}}>
<Text> toto</Text>
</View>
</TabBarIOS.Item>
<TabBarIOS.Item title="Tab #2"
selected={this.state.selectedTab === 'tab2'}
onPress={() => {
this.setState({ selectedTab: 'tab2', });
}}>
<ScrollView>
<View style={styles.galleryContainer}>
{
this.state.items.map((item) => {
return(
<View key={item.id} style={styles.pictureContainer}>
<Image source={{uri: item.src}} style={styles.picture} />
</View>
)
})
}
</View>
</ScrollView>
</TabBarIOS.Item>
</TabBarIOS>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
galleryContainer: {
flexWrap: 'wrap',
flexDirection: 'row',
flex: 1,
backgroundColor: 'red'
},
picture: {
height: width/2 - 4,
width: width/2 -4,
flex: 1,
},
pictureContainer: {
padding: 2
}
});
AppRegistry.registerComponent('TabIssue', () => TabIssue);

Resources