How to close the custom dropdown from outside the dropdown component? - javascript

I am using a Dropdown which is basically like below on many screens in the app. As you can see, on the screens I use this dropdown, the dropdown becomes 50 height when clicked and 20 when clicked again. On the screens where I use this dropdown, if the dropdown is on (height is 50), I want it to close when clicked somewhere other than the dropdown on the screen. Since I use the dropdown on so many screens, I only want to do this inside the dropdown component. How can I do that?
import React, { useState } from 'react';
import { View, TouchableOpacity } from 'react-native';
export default Dropdown = () => {
const [isOpen, setIsOpen] = useState(false);
return (
<View
style={{ backgroundColor: "grey" }}>
<TouchableOpacity
style={{ height: 20, width: 200 }}
onPress={() => setIsOpen(prev => !prev)}>
</TouchableOpacity>
<View style={{
display: isOpen ? 'flex' : 'none',
height: 50
}}>
</View>
</View>
);
};
How to close the custom dropdown from outside the dropdown component

You can use a modal to show the drawer contents. In this way, you can easily close the drawer on the screens where it is used.
import React, { useRef, useState } from 'react';
import { View, TouchableOpacity, Modal, TouchableWithoutFeedback, Text } from 'react-native';
export default Dropdown = () => {
const [isOpen, setIsOpen] = useState(false);
const [dropdownFrame, setDropdownFrame] = useState(null)
const dropdownRef = useRef()
const onPress = () => {
if (dropdownRef.current && dropdownRef.current.measure) {
dropdownRef.current.measure((fx, fy, width, height, x, y) => {
// required dropdown measure to display content directly below the dropdown
setDropdownFrame({ width, height, x, y })
setIsOpen(true)
})
}
}
return (
<View>
{/* drawer title */}
<TouchableOpacity
style={{ height: 30, backgroundColor: "grey" }}
onPress={onPress}
ref={dropdownRef}
>
<Text>Drawer Title</Text>
</TouchableOpacity>
{/* drawer content */}
{isOpen &&
<Modal transparent>
<TouchableWithoutFeedback onPress={() => setIsOpen(false)}>
<View style={{ flexGrow: 1 }}>
<View style={{
height: 40,
backgroundColor: "red",
top: dropdownFrame.y + dropdownFrame.height,
left: dropdownFrame.x,
}} >
<Text>item</Text>
</View>
</View>
</TouchableWithoutFeedback>
</Modal>}
</View>
);
};

Related

React Native - unable to pass state variable into style

My code fetches a json file, calculates a value and I want to pass this value into the style of TouchableOpacity. Below is my attempt:
const [height, setHeight] = useState(0)
const [isLoading, setLoader] = useState(true)
const fetching = async () => {
...//code that fetches the value
setHeight(value)
setLoader(false)
}
if (isLoading) {
return (
<Text> Loading...</Text>
)
}
return (
<View>
<TouchableOpacity
style={{height: height, width:30, backgroundColor: "red" }} />
... //other parts of the return statement
</View>
)
the complete code:
<View style={{height: height}}>
<TouchableOpacity
style={{width:30, borderWidth:5, marginTop:20, backgroundColor:"blue", height:height}}>
</TouchableOpacity>
</View>
Any help would be appreciated.
I think your useState is fine. However either the parent View doesn't have any space or the TouchableOpacity has nothing to display.
You can try to do:
return (
<View>
<TouchableOpacity
style={{height, width:30, borderWidth: 5 }} />
... //other parts of the return statement
</View>
)
If you see no border, then it's a problem with the parent View
You can then try:
return (
<View style={{flex: 1}}>
<TouchableOpacity
style={{height, width:30, borderWidth: 5 }} />
... //other parts of the return statement
</View>
)
You could also try adding a Text component with some text to the TouchableOpacity.
This code:
import React, { useEffect, useState } from 'react';
import {Text, View, TouchableOpacity} from 'react-native';
export default function App() {
const [height, setHeight] = useState(0)
const [isLoading, setLoader] = useState(true)
useEffect(() => {
const timerTask = setInterval(() => {
setHeight(Math.random() * 200)
setLoader(false);
}, 5000);
return () => clearInterval(timerTask);
}, [])
if (isLoading) {
return (
<Text> Loading...</Text>
)
}
return (
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<TouchableOpacity
style={{width: 30, borderWidth: 5, marginTop: 20, backgroundColor: "blue", height}}>
</TouchableOpacity>
</View>
)
}
Produces a blue touchable opacity that changes height every 5 seconds. Also when I touch it, it turns a lighter shade.
It seems to me you might not be passing the style property in the render function of TouchableOpacity, TouchableOpacity is your own custom component, isn't it?
const TouchableOpacity = ({ style }) => {
...
return (
<div style={style} >
...
</div>
);
}
Add a height and width to your element's style
height: 50,
width: '100%',
backgroundColor: "#000000"

tab navigation in react native is not working

import React from 'react'
import { View, Text, StyleSheet, TouchableWithoutFeedback } from "react-native";
const Header = props => {
const {
navigationState,
navigation,
activeTintColor,
inactiveTintColor
} = props;
const activeTabIndex = navigation.state.index;
return (
<View style={styles.containerHeader}>
<View style={styles.textContainer}>
<Text style={styles.textWhite}>Win Bets</Text>
<Text style={styles.textWhiteSmall}>win cash daily</Text>
</View>
<View style={styles.tabContainer}>
{navigationState.routes.map((route, index) =>{
const isRouteActive = index === activeTabIndex;
const tintColor = isRouteActive ? activeTintColor : inactiveTintColor;
return (
<TouchableWithoutFeedback
onPress={() => navigation.navigate(route.routeName)}
key={route.routeName}
>
<View>
<Text
style={{
fontSize: 17,
textTransform: "capitalize",
color: `${tintColor}`,
fontWeight: `${isRouteActive ? "700" : "normal"}`
}}
>
{route.routeName}
</Text>
</View>
</TouchableWithoutFeedback>
);
})}
</View>
</View>
);
};
the tabs show as they are supposed to be, but they are not clickable and cant navigate to a different page... also the home page doesnt show.
what could be the problems, or how do i configure the routes and routeName?

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.

React Native - Show keyboard and render its values without using an input

Is there a way to use a keyboard only without any text input and get its values onChange?
I would like to show the keyboard only on button click event and render its typing values in a view without any input.
What would be a correct way of implementing this?
You can add a dummy TextInput and onPress button set focus on TextInput to show keyboard . Save state with "onChangeText" prop and show in a View
Complete Code
import React from "react";
import { View, Text, Button, TextInput } from "react-native";
export default class App extends React.Component {
state = { text: "" };
render() {
return (
<View style={{ flex: 1, marginTop: 50 }}>
<TextInput
style={{ height: 0, width: 0, borderWidth: 0 }}
onChangeText={text => this.setState({ text })}
ref={ref => {
this.textInput = ref;
}}
autoCapitalize="none"
autoCorrect={false}
autoFocus={false}
/>
<Button
onPress={() => {
this.textInput.focus();
}}
title="Press Me To show Keyboard"
color="#841584"
/>
<View
style={{
borderColor: "red",
borderWidth: 1,
padding: 16,
marginTop: 20
}}
>
<Text style={{ marginBottom: 8 }}>Show Typing Values:</Text>
<Text>{this.state.text}</Text>
</View>
</View>
);
}
}
App Preview

Read the value of Parent state and pass Boolean to Child Component React

I'm having trouble learning how to pass data between parent and child in React Native.
In my parent component I have a state property (audioPlaying) which is a Boolean value.
state = {
//The title informs the Button and TitleArea components
title: 'hello',
audioPlaying: false,
};
I'd like to change that value on the press of a button (onPress).
<Button
title={this.state.title}
onPress={this.playPauseHandler}
audioPlaying={this.state.audioPlaying}
/>
...by calling the playPauseHandler.
playPauseHandler = () => {
this.setState(prevState => ({
audioPlaying: !prevState.audioPlaying
}));
}
Then in my child (Button) Component I want to evaluate the audioPlaying state property. If it's true, I want to show one things and false I want to show something else.
<View style={styles.playBtnStyle}>
{this.props.audioPlaying === false ? (
<MaterialIcons
name='play-arrow'
size={50}
color="#87888C"
/>
) : (
<MaterialIcons
name='pause'
size={50}
color="#87888C"
/>
)}
}
</View>
However, when I run this I get undefined for the value of audioPlaying.
React Native Error Message
Here are the full files for both:
App.js
import React, { Component } from 'react';
import { View, StatusBar } from 'react-native';
import Carousel from './src/components/Carousel/Carousel';
import Button from './src/components/Button/Button';
import TitleArea from './src/components/TitleArea/TitleArea';
import MapArea from './src/components/MapArea/MapArea';
const styles = {
container: {
flex: 1,
justifyContent: 'space-between',
},
playArea: {
flex: 1,
},
};
export default class App extends Component {
state = {
//The title informs the Button and TitleArea components
title: 'hello',
audioPlaying: false,
};
playPauseHandler = () => {
this.setState(prevState => ({
audioPlaying: !prevState.audioPlaying
}));
}
render() {
return (
<View style={styles.container}>
<TitleArea title={this.state.title} />
<StatusBar hidden={false} />
<Carousel />
<MapArea />
<Button
title={this.state.title}
onPress={this.playPauseHandler}
audioPlaying={this.state.audioPlaying}
/>
</View>
);
}
}
Button.js
import React, { Component } from 'react';
import { Text, View, TouchableOpacity, Dimensions } from 'react-native';
import MaterialIcons from 'react-native-vector-icons/MaterialIcons';
const { width } = Dimensions.get('window');
const height = width * 0.2;
const styles = {
textStyle: {
color: '#87888C',
fontSize: 18,
fontWeight: '600',
backgroundColor: 'white',
alignSelf: 'center',
},
buttonContainer: {
height,
flexDirection: 'row',
backgroundColor: 'white',
alignItems: 'center',
},
playBtnStyle: {
marginLeft: 50,
backgroundColor: 'white',
},
childStyle: {
flex: 1,
},
};
const button = (props) => {
return (
<View style={styles.buttonContainer}>
<TouchableOpacity>
<View style={styles.playBtnStyle}>
{this.props.audioPlaying === false ? (
<MaterialIcons
name='play-arrow'
size={50}
color="#87888C"
/>
) : (
<MaterialIcons
name='pause'
size={50}
color="#87888C"
/>
)}
}
</View>
</TouchableOpacity>
<View style={styles.childStyle}>
<Text style={styles.textStyle}>Chapter 1: {props.title}</Text>
</View>
</View>
);
}
export default button;
There is no this in the context of button. That is just a function returning JSX.
Instead, use props
<View style={styles.playBtnStyle}>
{props.audioPlaying === false ? (
<MaterialIcons
name='play-arrow'
size={50}
color="#87888C"
/>
) : (
<MaterialIcons
name='pause'
size={50}
color="#87888C"
/>
)}
</View>
Ok so I solved my own problem! (step one to being a developer)
Two issues:
Capturing Touch Events
React Native has what's called Touchables. According to the documentation these are "wrappers that make views respond properly to touches".
TouchableOpacity, the one I'm using:
On press down, the opacity of the wrapped view is decreased, dimming it. Opacity is controlled by wrapping the children in an Animated.View, which is added to the view hierarchy.
https://facebook.github.io/react-native/docs/touchablewithoutfeedback#onpress
All Touchables accept the onPress prop. So by adding the onPress prop to the Touchable, I'm able to capture the touch event instead of just firing it.
Passing Callback to Parent
This article helped me understand more about how a parent function can be called from a child.
https://medium.com/#thejasonfile/callback-functions-in-react-e822ebede766
So I'm calling playPause() (I renamed the prop and destructured it) in TouchableOpacity, which fires from a touch event causing state to change and component to re-render.
const button = (props) => {
const {
title,
audioPlaying,
playPause,
} = props;
return (
<View style={styles.buttonContainer}>
<TouchableOpacity onPress={() => playPause()}>
<View style={styles.playBtnStyle}>
{audioPlaying === false ? (
<MaterialIcons
name='play-arrow'
size={50}
color="#87888C"
/>
) : (
<MaterialIcons
name='pause'
size={50}
color="#87888C"
/>
)
}
</View>
</TouchableOpacity>
<View style={styles.childStyle}>
<Text style={styles.textStyle}>
Chapter 1:
{title}
</Text>
</View>
</View>
);
};

Categories

Resources