React-Native-snap-carousel check condition with state value? - javascript

I'm working on react native app. For slider part I put "Snap carousel" plugin. My slider is showing multiple item. By the time calculate slider item with divide by window width/5. Now I got 5 slider item on display.
The problem is, I need to show slider text for active slider item and change background color.
In render item I can't use state or props value. How can I check with current index value in render item method?
constructor(props) {
super(props);
this.state = {
activeSlide:0
}
}
_renderItem ({item, index, currentIndex}) {
const self = this;
return (
<View style={styles.slide}>
<View style={stylesSelect.carouselImg}>
<Image
source={item.src}
resizeMode='cover'
/>
</View>
<Text>
{item.title}
</Text>
</View>
);
}
render() {
const { data } = this.props;
return (
<View style={styles.container}>
<Carousel
ref={(c) => { this._carousel = c; }}
data={data}
renderItem={this._renderItem}
sliderWidth={width}
itemWidth={Math.round(width/5)}
sliderHeight={height}
itemHeight={height}
inactiveSlideScale={0.8}
inactiveSlideOpacity={0.7}
loop={true}
onSnapToItem={(index) => this.setState({ activeSlide: index }) }
/>
</View>
)
}
I want to show the {item.title} Text for active slider item only.

renderItem = data =>
<View>
<TouchableOpacity >
{/* {console.log("==================================")}
{console.log(data)} */}
<ImageLoad
style={{ height: 200, width: itemWidth, borderRadius: theme.sizes.itemRadius }}
placeholderSource={require('./images/logo.png')}
source={{ uri: data.item.thumbnail }}
resizeMode={'contain'}
isShowActivity={true}
borderRadius={theme.sizes.itemRadius}
placeholderStyle={{ height: 200, width: itemWidth, borderRadius: theme.sizes.itemRadius }}
/>
<View style={styles.featuredDiv}><Text style={styles.title}>{data.item.title}</Text></View>
</TouchableOpacity>
</View>

Related

How to get current slide position or number in react-native flatlist

Hy, I'm creating a slider in react-native. It works perfectly. It having three slides on scrolling. I done this using Animated.FlatList and scrollX. But One thing I'm not getting is how I can know which slide is currently displaying. I want to because it onboard screen I want to display the skip button on the first two slides and on the third I want to display something else button how I can do that.
const OnBoardScreen = () => {
const scrollX = useRef(new Animated.Value(0)).current;
// flatlist render function
const renderSplash = ({ item, index }) => {
return (
<View style={styles.mainWrapper}>
<View style={styles.imageWrapper}>{item.image}</View>
<View style={[styles.titleWrapper,
{marginTop:index==1 ? hp(4):index == 2 ? hp(10):null}
]}>
<Text style={styles.titleText}>{item.title}</Text>
</View>
<View style={styles.bodyWrapper}>
<Text style={styles.bodyText}>{item.body}</Text>
</View>
</View>
);
};
return (
<View style={styles.container}>
{/* background Svgs */}
<View style={styles.blueSvgWrapper}>
<BlueSvg />
</View>
<View style={styles.dotSvgWrapper}>
<DotsSvg />
</View>
<View style={styles.orangeSvgWrapper}>
<OrangeSvg />
</View>
{/* Main Content */}
<Animated.FlatList
data={onBoardData}
keyExtractor={(item, index) => item.key}
renderItem={renderSplash}
horizontal
scrollEventThrottle={32}
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { x: scrollX } } }],
{ useNativeDriver: false }
)}
pagingEnabled
showsHorizontalScrollIndicator={false}
/>
<Indicator scrollX={scrollX} />
{skip == false ?
<TouchableOpacity style={styles.skipWrapper} >
<Text style={styles.skipText} >Skip</Text>
</TouchableOpacity>
: null }
</View>
);
};
I try to do conditions on scrollX but it's not updating the value like states do.
My Issue:
How to know apply condition on slides which thing should be show or which not?
FlatList inherits all of ScrollView props.
Therefore you can use onScroll.
onScroll = (event) => {
const scrollOffset = event.nativeEvent.contentOffset.y
}

React Navigation only sets param after navigating a second time

I want to pass the item (asset) from a Flastlist and present in a child screen.
But when I press the item, the parameter is null. I have to go back and press it again for the parameter to be set. And if I press a different item, the old item still lingers until I press the new item a second time.
I don't know if useEffect is the best way to do it. I am just trying different approaches but have not had any luck with useEffect, useFocusEffect, or none.
Parent with the Flastlist
export default function SitesScreen(props) {
const [sites, setSites] = useState(["Power Plant", "Paper Mill", "Plastic Injection"])
const [selectedItem, setSelectedItem] = useState(null)
const Item = ({ item, onPress }) => (
<TouchableOpacity onPress={onPress} style={[styles.item]} >
<Text style={styles.text}>{item}</Text>
</TouchableOpacity>
)
const renderItem = ({ item }) => {
return (
<View style={styles.itemContainer} >
<Item
item={item}
onPress={() => onItemSelected(item)}
/>
</View >
)
}
const onItemSelected = (item) => {
setSelectedItem(item)
props.navigation.navigate("Asset", { asset: selectedItem })
}
return (
<View style={styles.container}>
<CustomHeader title="Sites" navigation={props.navigation} isHome={true} ></CustomHeader>
<View style={styles.contentContainer}>
<View style={{ width: '90%', height: '50%', alignItems: 'center', bottom: -150 }} >
<FlatList
data={sites}
renderItem={renderItem}
keyExtractor={(item) => JSON.stringify(item)}
/>
</View>
</View>
</View>
)}
Child screen to present item
export default function SitesScreen(props) {
const [asset, setAsset] = useState('')
useEffect(() => {
setAsset(props.route.params.asset)
console.log(asset)
}, [])
return (
<View style={styles.container}>
<CustomHeader title="Asset" navigation={props.navigation} isHome={false} ></CustomHeader>
<View style={styles.contentContainer}>
<Text style={styles.text} >{asset}</Text>
<View style={{ width: '90%', height: '50%', alignItems: 'center', bottom: -150 }} >
</View>
</View>
</View>
)}
When you pass the selectedItem value as a parameter to the next screen, the new state set from setSelectedItem has not been applied to the component yet. Because the new state hasn't been applied, you are still passing the initial null value that was set for selectedItem. This happens because state values are used by functions based on their current closures.
See this StackOverflow post for a more detailed explanation about this problem.
Problem solved.
The parent screen needs to have the navigate action in the useEffect hook, and not in the onItemSelected function. That way it waits until the state has been changed before it navigates.
useEffect(() => {
if (selectedItem) {
props.navigation.navigate("Asset", { asset: selectedItem })
}
}, [selectedItem])

Filtering items when a feild is pressed in react native

I have a screen with different cards (that has article information) am trying to filter the articles by categories when ever the convenient category is pressed i want that category to be selected and the articles that belong to that category to show and , on the other hand all articles from all the categories to show when no category is selected ( this will make more sense if you look at the picture bellow )
The code used to showing the pictures of the diffrent categories :
import TouchableScale from "react-native-touchable-scale";
import { category } from "../api/data";
import colors from "../config/colors";
function HotTopics({ navigation }) {
//const { width, height } = Dimensions.get("window");
return (
<View style={styles.Container}>
<View>
<Text style={styles.CategoryText}>Hot Topics</Text>
</View>
<FlatList
horizontal
showsHorizontalScrollIndicator={false}
style={{ paddingHorizontal: 15 }}
data={category}
keyExtractor={(item) => item.id}
renderItem={({ item }) => {
return (
<View>
<View>
<TouchableScale
activeScale={0.9}
tension={50}
friction={7}
useNativeDriver
onPress={() => navigation.navigate({ id: item.id })}
>
{/* to show the horizental news list*/}
<Image
source={{ uri: item.image }}
style={{
width: 100,
height: 120,
borderRadius: 16,
marginRight: 10,
}}
/>
{/* to show the news titles inside the pictures*/}
<SharedElement
id={`item.${item.id}.text`}
style={{
width: 100,
position: "absolute",
bottom: 95,
//left: 10,
paddingHorizontal: 5,
justifyContent: "center",
alignItems: "center",
}}
>
<Text style={styles.blogTitle}>{item.title}</Text>
</SharedElement>
{/* to show the pictre of the author of the news article*/}
{/* to show the name of the author and read time of article*/}
</TouchableScale>
</View>
</View>
);
}}
/>
</View>
);
}
The code used for showing the articles cards bellow : (ArticleList.js)
function ArticleList({ navigation, post }) {
if (!post.length) {
return null;
} // so we dont show anything untill we have articles
return (
<>
<View style={styles.Container}>
<Text style={styles.HeadText}>Popular</Text>
<Text style={styles.subText}>Show all</Text>
</View>
<FlatList
showsVerticalScrollIndicator={false}
data={post}
keyExtractor={(item) => item.id}
renderItem={({ item }) => {
return (
<TouchableScale
activeScale={0.9}
tension={50}
friction={7}
useNativeDriver
onPress={() =>
navigation.navigate("DetailScreen", { data: item })
}
>
<Card item={item} />
</TouchableScale>
);
}}
/>
</>
);
}
And on the main screen i call ArticleList.js and filter the data like this :
// for filtering the data
const filterResultsByCategory = (category) => {
return post.filter((onepost) => {
return onepost.category === category;
});
};
// to show the data
<ArticleListVer post={filterResultsByCategory("Politics")} />
the code used for the main screen :
import React, { useState } from "react";
import { StyleSheet, View, ScrollView, SafeAreaView } from "react-native";
import Header from "../components/Header";
import ArticleList from "../components/ArticleList";
import ArticleListVer from "../components/ArticleListVer";
import Categories from "../components/Categories";
import HotTopics from "../components/HotTopics";
import { LinearGradient } from "expo-linear-gradient";
import useArticles from "../hooks/useArticles";
import usePosts from "../hooks/usePosts";
function MainScreen({ navigation }) {
const [Category, setCategory] = useState();
const [loadApi, data, errorMessage] = useArticles();
const [loadPost, post] = usePosts();
const filterResultsByCategory = (category) => {
return post.filter((onepost) => {
return onepost.category === category;
});
};
return (
<View style={{ flex: 1 }}>
{/* header Date and title */}
<LinearGradient
colors={["#353540", "#353540", "#1E1E24"]}
style={{ width: "100%", height: "100%" }}
>
<SafeAreaView style={styles.container}>
<ScrollView style={styles.scrollView}>
<Header headerTitle="TODAY'S ARTICLES" />
{/* haeder Categories */}
<Categories />
{/* show the data in a flatlist */}
<ArticleList data={data} navigation={navigation} />
{/* show the categories in a flatlist*/}
<HotTopics onCategorySelect={this.setCategory} />
{/* show the vertical Article list */}
<ArticleListVer
post={filterResultsByCategory(this.state.category)}
/>
</ScrollView>
</SafeAreaView>
</LinearGradient>
</View>
);
}
MainScreen.navigationOptions = () => {
return {
headerShown: false,
};
};
const styles = StyleSheet.create({});
export default MainScreen;
The easiest way to handle this is to have a callback function to set the state from the HotTopics component something like below
const [category, setCategory] = useState();
In render
<HotTopics onCategorySelect={setCategory} />
<ArticleListVer post={filterResultsByCategory(category)} />
for the onclick of the hottopic you can do
onPress={() =>
this.props.onCategorySelect(item.category)
}
By doing this you will re render the parent with new Category. and for reset you will need a button to reset the state to empty so that you will show all items.

How to avoid jsx styling error in react native?

Whenever I tried to build UI from scratch I'm getting this error adjacent jsx element must be wrapped in an enclosing tag. I don't know how to solve this. Because I tried different methods, I've tried to put the blocks within View component withflex:1 but non-use. Is there any proper solution for this. This is becoming a great challenge for me because I can't design any components of my own. What to do please help me. Following is my code.
screen.js
export default class FirstScreen extends Component {
constructor(props){
super(props);
this.state = {
showPopupDialog: false,
workType: "",
workers: []
}
}
componentWillMount(){
fetch('http://192.168.1.6:3000/api/worker', {
method:'GET',
headers:{
Accept: 'application/json'
}
})
.then(response => response.json())
.then(responseData =>
this.setState({
workers:responseData
})
)
}
onPressYes = (workType) => {
console.log(workType);
}
popupDialog = (id, workType) => {
this.setState ({
showPopupDialog: true,
workType: workType
});
//make sure you set showPopupDialog to false and workType to "" when you click yes or no button in PopupDialog component so that it will work the next time you click on card
}
render() {
const { workers, workType, showPopupDialog} = this.state;
return (
<View style={{flex:1}}>
<Header />
<ScrollView>
{workers.map((a, index)=> (
<View style={{flex:1}}>
<CardSection>
<TouchableOpacity onPress={() => this.popupDialog(a.id, a.work_type)}>
<View style={{ maringTop: 10, marginLeft:120}}>
<Image style={{ height: 100, width: 100 }} source={{ uri: a.work_type == 'Carpenter' ? images[0].image : images[1].image}}/>
<Text style={{marginLeft:20, fontSize:20}}>{a.work_type}</Text>
</View>
</TouchableOpacity>
</CardSection>
</View>
))}
{showPopupDialog && <PopupDialog
dialogStyle={{ backgroundColor: "#FFFFFF", height: 180, width:300, borderWidth:1,padding:10}}
overlayBackgroundColor="#fff" dismissOnTouchOutside={true}>
<View style={styles.dialogContentView}>
<Text style={{fontSize:18, margingTop:10,color:"#000000"}}>Are you sure you want to submit?</Text>
<View style={{flexDirection:'row'}}>
<View style={styles.button_1}>
<Button title="Yes" color="#FF6633" onPress={() => this.onPressYes(workType)}/>
</View>
<View style={styles.button_1}>
<Button title="No" color="#FF6633" onPress={() =>this._onPressNo() }/>
</View>
</View>
</View>
</PopupDialog>}
</ScrollView>
</View>
);
}
}
The issue I'm facing is I can't place the <PopupDialog> component adjacent to <CardSection> , in order to that I put the <PopupDialog> within <View> ,even though it doesn't solve my issue.Please help..Please
Give a try with below corrected code.
There are two things that needs be corrected
You are doing .map but you are not returning anything which I have
corrected in the code below
export default class FirstScreen extends Component {
constructor(props){
super(props);
this.state = {
workType: "",
workers: []
}
}
componentWillMount(){
fetch('http://192.168.1.6:3000/api/worker', {
method:'GET',
headers:{
Accept: 'application/json'
}
})
.then(response => response.json())
.then(responseData =>
this.setState({
workers:responseData
})
)
}
onPressYes = (workType) => {
console.log(workType);
}
popUpDialog = (id, workType) => {
this.setState ({
workType: workType
});
this.popupDialog.show();
//make sure you set workType to "" when you click yes or no button in PopupDialog component so that it will work the next time you click on card
}
render() {
const { workers, workType} = this.state;
return (
<View style={{flex:1}}>
<Header />
<ScrollView>
{workers.map((a, index)=> (
<View style={{flex:1}}>
<CardSection>
<TouchableOpacity onPress={() => this.popUpDialog(a.id, a.work_type)}>
<View style={{ maringTop: 10, marginLeft:120}}>
<Image style={{ height: 100, width: 100 }} source={{ uri: a.work_type == 'Carpenter' ? images[0].image : images[1].image}}/>
<Text style={{marginLeft:20, fontSize:20}}>{a.work_type}</Text>
</View>
</TouchableOpacity>
</CardSection>
</View>
))}
<PopupDialog ref={popupDialog => {
this.popupDialog = popupDialog;
}}
dialogStyle={{ backgroundColor: "#FFFFFF", height: 180, width:300, borderWidth:1,padding:10}}
overlayBackgroundColor="#fff" dismissOnTouchOutside={true}>
<View style={styles.dialogContentView}>
<Text style={{fontSize:18, margingTop:10,color:"#000000"}}>Are you sure you want to submit?</Text>
<View style={{flexDirection:'row'}}>
<View style={styles.button_1}>
<Button title="Yes" color="#FF6633" onPress={() => this.onPressYes(workType)}/>
</View>
<View style={styles.button_1}>
<Button title="No" color="#FF6633" onPress={() =>this._onPressNo() }/>
</View>
</View>
</View>
</PopupDialog>
</ScrollView>
</View>
);
}
}
If I understand your question correctly...
You can return multiple root elements in jsx by wrapping is in a <React.Fragment> element (you can use <> and </> in v16.2 and later). Fragments are new in React v16. Prior to that, you just have to wrap them in some element (a div or span, usually).
The problem is that you have this structure:
<a>
{this.state.workers.map((a, index)=>
<b/>
<c/>
)}
</a>
Since <b/><c/> is parsed separately and there's no enclosing element, you're getting the error. But an enclosing element isn't necessary for the final structure, which does have an enclosing element. The solution is to simply return an array of JSX elements, like this:
<a>
{this.state.workers.map((a, index)=>
[<b/>,
<c/>]
)}
</a>

border bottom changing on press flatlist item in react native

i'm new in react native and i need your help
i want to give border-bottom to pressed item of horizontal flatlist and border-bottom of Previous item disappear,
now i can give border to new pressed item but i cant remove previous item border
how can i achive this?
enter image description here
these are my Category_style code
state = {
isModalVisible: false,
Index : 0
}
_toggleModal = (index) => {
this.setState({isModalVisible: !this.state.isModalVisible});
this.setState({Index : index});
}
renderProduct(item) {
return <Sub_Categories_FlatList_style name={item.title} icon={item.icon}/>
}
renderSeparator = () => (
<View
style={{
backgroundColor: '#d2d2d2',
height: 0.5,
}}
/>
)
render() {
const {title, index} = this.props;
return (
<View style={pStyles.container}>
<TouchableHighlight onPress={() => this._toggleModal(index)}
style={(index === this.state.Index) ? pStyles.border_bottom : pStyles.no_border_bottom}>
<Text style={pStyles.title}>{title}</Text>
</TouchableHighlight>
<Modal isVisible={this.state.isModalVisible} animationType={'slide'}>
<TouchableOpacity onPress={() => this._toggleModal(index)} style={pStyles.T_opacity}
activeOpacity={.7}>
<Image source={require('./../pictures/x_icon.png')}
style={pStyles.close_image}/>
</TouchableOpacity>
<View style={pStyles.in_modal_view}>
<Text style={pStyles.modal_header_text}>{title}</Text>
<FlatList
data={this.props.subCategory}
renderItem={({item}) => this.renderProduct(item)}
ItemSeparatorComponent={this.renderSeparator}
keyExtractor={(item, index) => index}/>
</View>
</Modal>
</View>
and these are my Category code
static navigationOptions = {
headerStyle: {
backgroundColor: '#4caf50'
},
headerTitle: <Text style={Category_in_style.headerTitleStyle}>Category</Text>
}
renderCategory(item, index) {
return <Category_style title={item.title} index={index} subCategory={item.sub_category}/>
}
renderProduct(item) {
return <Product_style image={item.imageUrl} title={item.title} price={item.price}/>
}
render() {
return (
<View style={{backgroundColor: 'white'}}>
<FlatList style={styles.first_flat}
horizontal
data={this.state.main_categories}
renderItem={({item, index})=> this.renderCategory(item, index)}
keyExtractor={(item, index) => index}/>
<PTRView style={{backgroundColor: '#f1f1f1'}}>
<FlatList style={[{marginTop: 10}, {marginBottom: 50}]}
data={this.state.articles}
renderItem={({item}) => this.renderProduct(item)}
keyExtractor={(item, index) => index}
numColumns={2}/>
</PTRView>
</View>
There may be multiple ways of doing this but I prefer having the parent decide which component is clicked.
So what I suggest is in your renderProduct the item object also has an index which you can pass to your Sub_Categories_FlatList_style and also pass a function to it which basically updates a variable whenever the item is clicked.
Then when you render the list item simply check if it's index matches the currently selected index and then style it accordingly.
Sorry if this seems a bit vague but I'm eyeballing this since I'm at work but I'll be more than happy to answer any follow ups.

Categories

Resources