I am working on an onPress event in React Native. I have added two different actions onPress, onLongPress. I have two different functions associated with each of them. Now i have also added delayLongPress={250} and it's working with onLongPress. But when i try to call the onLongPress, it calls the onPress too. I don't want that to happen. I want to call the onpress when it's pressed just once and onLongPress when it's pressed for 250ms at least. How can i seperate those function calls.
Here's what i have right now:
const onLongPress = () =>{
console.log('Pressed long')
}
const onChange = () =>{
console.log('Pressed')
}
return(
<Container
onPress={onChange}
onLongPress={onLongPress}
delayLongPress={250}
>
</Container>
)
Try to wrap it with the TouchableHighlight.
export default function App() {
const onLongPress = () => {
console.log('Pressed long');
};
const onChange = () => {
console.log('Pressed');
};
return (
<TouchableHighlight
onPress={onChange}
onLongPress={onLongPress}
delayLongPress={250}
>
<Text>Press</Text> // Your child components goes here
</TouchableHighlight>
);
}
See the snack here
Related
function A() {
const [levels, setLevels] = React.useState(null);
// upon the component mount, we call the database.
React.useEffect(() => {
database()
.ref()
.once("value")
.then((snapshot) => {
//get Levels from db
const levels = snapshot.map((child) => child.key);
setLevels(levels);
});
}, []);
return (
<View>
{levels.map((element) => {
return <B level={element} />;
})}
</View>
);
}
function B(props){
const navigation = useNavigation();
return (
<View>
<TestButton
title={props.level}
onPress={() => navigation.navigate('C')}
/>
</View>
);
}
I currently am already passing the value 'level' to the TestFunc parameter, but now I wish to pass the same parameter to the screen I navigate to when I press the TestButton component.
https://reactnavigation.org/docs/params/
This documentation shows me how I would do such with a newly initialized parameter, but it doesn't seem to work with parameters that have been passed from previous screens.
Any suggestions?
EDIT:
I have tried to make it a little bit clearer. I want to go from function A to B to C, all different components/screens in my app. I want to take the 'level' that I obtain from function A and pass it from B to C. Currently I am correctly retrieving the 'level' value in function B, however I can not seem to pass it to function C.
function TestFunc (props){
const navigation = useNavigation();
return (
<View>
<TestButton
title={props.level}
onPress={() => navigation.navigate('Category',{...props})}
//{...props} send all props that's exist in props argument
/>
</View>
);
}
You can get the all props on Category component also
const { level } = this.props.navigation.state.params;
So I'm simply trying to toggle the visibility of a React component on a specific keypress.
This is working perfectly, but it seems every Time i toggle the component visibility using ctrl+`, the process gets slower and slower, and my console.log(e) from the onKey function in my app component gets called 10,50,100, 1000 times etc.
This is what I have so far
ReactDOM.render(
<React.StrictMode>
<Router>
<App />
</Router>
</React.StrictMode>,
document.getElementById('root')
);
function App() {
const [compHidden, toggleComp] = useState(true);
const onKey = (e) => {
console.log(e)
if(e.ctrlKey && e.key === '`') {
toggleComp(!compHidden);
}
}
document.addEventListener('keydown', onKey);
return (
<ComponentToHide hidden={compHidden}/>
);
}
function ComponentToHide(props) {
return (
<div style={{display: props.hidden ? 'none' : 'flex'}}>
<p>Visible</p>
</div>
)
}
Do not use document.addEventListener inside the render function, as that function is called every time your component renders and it will add a new event listner each time.
Instead, use the React Hook React.useEffect() to add the event listner only once as the component is mounted:
function App() {
const [compHidden, setCompHidden] = useState(true);
useEffect(() => {
const onKey = (e) => {
console.log(e)
if(e.ctrlKey && e.key === '`') {
setCompHidden(value => !value);
}
}
document.addEventListener('keydown', onKey);
return () => document.removeEventListener('keydown', onKey);
}, [])
return (
<ComponentToHide hidden={compHidden}/>
);
}
Two things to note:
useEffect's second argument, just an empty array, tells React only to run that function once at the beginning of the components lifecycle
You can return a cleanup function that is ran as your component is unmounted. We use this here to remove the event listener to fix an additional memory leak.
I am following the functional pattern as given by the expo project creation wizard, and I have a component like this:
Search.js
export default function Search() {
const [searchResults, setSearchResults] = React.useState(buildContentViews(contents));
return (
<View style={styles.container}>
<ScrollView contentContainerStyle={styles.contentContainer}>
<View style={styles.statusLine}>
<Text style={styles.statusLineText}>{(pageInfo.numResults || 0) + ' Treffer'}</Text>
</View>
{searchResults}
</ScrollView>
</View>
);
}
Now I have some non-react-native implementation for backend REST-services, which shall regularly update the search results. Therefore I would need to do something like:
export default function Search() {
const [searchResults, setSearchResults] = React.useState(buildContentViews(contents));
client.events.on('searchResults', (results) => setSearchResults(results));
return (
<View style={styles.container}>
<ScrollView contentContainerStyle={styles.contentContainer}>
<View style={styles.statusLine}>
<Text style={styles.statusLineText}>{(pageInfo.numResults || 0) + ' Treffer'}</Text>
</View>
{searchResults}
</ScrollView>
</View>
);
}
However I quickly get the error that too many event listeners are established, which is probably because the above code gets run every time the component is (re-)rendered, or in other words, whenver the component is updated.
So how would I correctly register the event listener and/or deregister the event listener in this scenario?
useEffect hook is your friend here!
This is how I handle registering/deregistering to a react-navigation event moving between the screens (I don't know how your client code works, this is just an example)
useEffect(() => {
const onFocus = () => {
// here I do something when the screen gets focused
}
// this is how you handle the registration to the event
const focusListener = navigation.addListener('didFocus', onFocus)
// and this is how to handle deregistration!
return () => focusListener.remove()
}, []) // empty array of dependencies
In the body of the useEffect hook, you define your actions;
the return function is used to clean-up effects, the perfect place to remove an event listener;
the empty array of dependencies assures you that this code will be executed just one time (after the first render) and no more! No more reallocation! The perfect way to define an event listener!
So I have this Display() function which takes events from the Google Calendar and the function returns a list of elements (each element is associated with a slider) to be rendered on the screen (see return statement of Display() function) and renders them as seen here. So each element comes with a Remove button so that I can remove an unwanted element from the page using the hideMe() function inside the Display() function. The hideMe() function does seem to do its work, however, it removes all the elements on the page as seen here. So I am struggling to figure out what I should fix so that when I click on the Remove button, it only removes the element and the slider associated to that remove button. I am new to React and JavaScript so please help. Any help is appreciated and thank you in advance.
function Display() {
const isMounted = useRef(true);
const [items, saveItems] = useState([]);
const [visible, setVisible] = useState(true);
const [fading, setFading] = useState(false);
useEffect(() => {
return () => {
isMounted.current = false;
};
}, []);
useEffect(() => {
(async () => {
const items = await fetchItems();
//Do not update state if component is unmounted
if (isMounted.current) {
saveItems(items);
}
})();
}, []);
function hideMe() {
setFading(true);
setTimeout(() => setVisible(false), 650);
}
return (
<Tab.Pane attached={false}>
<h5>Rate stress level for each event</h5>
<br/>
{items.map(item => (
<div key={item.id} isvisible={!fading}
style={visible ? null : { display: "none" }}>
<Typography id="discrete-slider-restrict" gutterBottom>
{item}
<button onClick={hideMe}>Remove</button>
</Typography>
<PrettoSlider aria-label="pretto slider" defaultValue={98} step={null}marks={stresslevel}/>
</div>
))}
</Tab.Pane>
)
}
It seems to me that this issue is happening because all elements are available in same state or i would say that they all share same state. So, this executes for all. If it is possible for you to extract it to a new component and use the hideMe function there. This will i am sure work for each individual elements.
It is my suggestion please go through below. May be you have to tweak a little bit.
You can extract the elements in a separate component like:
const Item = props => {
const [visible, setVisible] = useState(true);
const [fading, setFading] = useState(false);
function hideMe() {
setFading(true);
setTimeout(() => setVisible(false), 650);
}
return (
<div isvisible={!fading} style={visible ? null : { display: "none" }}>
<Typography id="discrete-slider-restrict" gutterBottom>
{item}
<button onClick={hideMe}>Remove</button>
</Typography>
<PrettoSlider aria-label="pretto slider" defaultValue={98}
step={null} marks={stresslevel}/>
</div>
);
};
export default Item;
Then you can use it like:
// import Item
{items.map(item => (
<Item key={item.id} itemObj={item} />
// in case if you need item obj then props.itemObj will get you the object.
))}
In this way you can manage the hideMe function with the separate specific Item component.
I am trying to capture all click events outside of my SearchBar component so that I can then tell the dropdown menu to close when one clicks out of it. I looked up examples of how to do this online and I need to use the global variable 'document' in javascript. However, it seems react native does not support this. Does anyone know a work around to use the 'document' variable or a react native equivalent?
class Products extends Component {
constructor(props) {
super(props);
this.setWrapperRef = this.setWrapperRef.bind(this);
this.handleClickOutside = this.handleClickOutside.bind(this);
}
setWrapperRef(node) {
this.wrapperRef = node;
}
handleClickOutside(event) {
if (this.wrapperRef && !this.wrapperRef.contains(event.target)) {
alert('You clicked outside of me!');
}
}
componentWillMount() {
this.props.dispatch(getProductList());
}
componentDidMount() {
document.addEventListener('mousedown', this.handleClickOutside);
}
componentWillUnmount() {
document.removeEventListener('mousedown', this.handleClickOutside);
}
render() {
const {isLoading, products} = this.props.products;
if (isLoading) {
return <Loader isVisible={true}/>;
}
return (
<View ref={this.setWrapperRef} style={styles.wrapper}>
<Header/>
<View style={styles.bodyWrapper}>
<ScrollView style={styles.scrollView}>
<ProductsContainer data={{productsList: { results: products }}}/>
</ScrollView>
<SearchBar style={styles.searchBar}/>
</View>
<Footer/>
</View>
);
}
}
function mapStateToProps(state) {
const {products} = state;
return {
products
};
}
export default connect(mapStateToProps)(Products);
You can't use document, it's an object on the window. The above answer is incorrect and hasn't taken into account this platform is React Native (answer has since been removed).
To handle click events, you you need to wrap everything in a TouchableWithoutFeedback.
<TouchableWithoutFeedback
onPress={this.hideSearchBar}
/>
I would add a zIndex style to the TouchableWithoutFeedback and one in styles.scrollView. Make sure the zIndex inside of styles.scrollView is more than the one you added to the TouchableWithoutFeedback.