animate height with react native reanimated 2 - react-native

I'm trying to create a drop down component and I need opening drop down done with animation of it's height. I used reanimated 2 library for my project and I'm wondering why it is not working:
const dropDownOptionsHeight = useSharedValue(0);
const animatedStyles = useAnimatedStyle(() => {
console.log(dropDownOptionsHeight.value);
return {height: dropDownOptionsHeight.value + 120};
});
//this is my drop down box container:
const dropDownOptionsContainer = useCallback(() => {
return (
<Box
style={[styles.dropDownOptionsContainer, animatedStyles]}
>
<Text variant='regular'>hello</Text>
</Box>
);
}, [animatedStyles, styles.dropDownOptionsContainer]);

Related

how do you recognize touch events on react native webview?

Why is longpress event not recognizing the long press event? I have also used gestureHandler component and both are not working.
Can any one point me to relevant documentation or tell me why this code is not recognizing touchevents on the webview component.
export default function SiteCard({route, navigation}) {
const { site } = route.params;
const [urls, setUrls] = useState({
url2: site,
})
const webViewRef = useRef();
const onLongPress = (event) => {
if (event.nativeEvent.state === State.ACTIVE) {
alert("I've been pressed for 800 milliseconds");
}
};
return (
<LongPressGestureHandler
onHandlerStateChange={onLongPress}
minDurationMs={200}
>
<ScrollView scrollEnabled={false}
style={styles.tab}
level='1'>
<View>
<WebView
source={{ uri: urls.url2 }}
style={styles.PageView}
ref={webViewRef}
/>
</View>
</ScrollView>
</LongPressGestureHandler>
);
};
const styles = StyleSheet.create({
PageView: {
height: 800,
}
});

React Native BigList stopping and not calling 'onEndReached'

I'm very new to React Native and I'm having an issue using the BigList.
After four or five scrolls to the bottom of the list it just stops retrieving any more data, even though there is plenty more data in the DB. I can still scroll the list up and down but it's like the 'onEndReached' is no longer being fired for some reason.
I've done a Google search but nobody else seems to be having this problem so I assume the issue is with my code.
Here's my code:
import React, { useEffect, useState } from 'react';
import { View, Text, Image, Dimensions } from 'react-native';
import BigList from 'react-native-big-list';
const HomeScreen = ({ navigation }) => {
const BaseURL = "http://localhost:12646/"
const windowWidth = Dimensions.get('window').width;
const [homeImages, setHomeImages] = useState([]);
const [pageNumber, setOffset] = useState(1);
const [loading, setLoading] = useState(false);
useEffect(() => {
getData();
}, []);
const getData = () => {
if (!loading) {
setLoading(true);
var url = BaseURL + 'api/images/GetHomepageImages?PageNumber=' + pageNumber + '&PageSize=3';
fetch(url)
.then((response) => response.json())
.then((responseJson) => {
setOffset(pageNumber + 1);
setHomeImages([...homeImages, ...responseJson]);
setLoading(false);
})
.catch((error) => {
console.error(error);
});
}
}
const renderItem = ({ item, index }) => {
return (
<View style={{ height: windowWidth }}>
<Image
style={{ width: windowWidth, height: windowWidth, }}
source={{ uri: BaseURL + "images/" + item.basePath + "/" + item.imageSquare }}
/>
</View>
);
}
return (
<View style={{flex: 1}}>
{
(homeImages != null) ?
<BigList
data={homeImages}
onEndReached={getData}
itemHeight={windowWidth}
renderItem={renderItem}
/>
: <Text>Loading</Text>
}
</View>
)
};
export default HomeScreen;
If anyone else is stuck on this I think I've fond the solution. Adding the following line as one of the options in the BigList appears to have fixed it: -
onEndReachedThreshold={1}
Since adding this it thankfully hasn't happened again

React-Native Animated Accordion/ Drawer/ Drop-down/ Collapsible-card

I want to implement an animated accordion list/ drawer / drop-down menu / collapsible card.
The animation should be performant and look like this:
After a lot of searching, I could find many libraries. But I wanted to implement it without any library. Also, some tutorials showed how to build one, but they were not performant.
Finally, this is how I implemented it. The complete snack code is here: https://snack.expo.dev/#vipulchandra04/a85348
I am storing isOpen (whether the menu is open or closed) in a state. Then changing that state on button press. I am using the LayoutAnimation API in React-Native to animate the opening and closing of the list. LayoutAnimation runs the animation natively, thus it is performant.
const Accordion = ({ title, children }) => {
const [isOpen, setIsOpen] = useState(false);
const toggleOpen = () => {
setIsOpen(value => !value);
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
}
return (
<>
<TouchableOpacity onPress={toggleOpen} activeOpacity={0.6}>
{title}
</TouchableOpacity>
<View style={[styles.list, !isOpen ? styles.hidden : undefined]}>
{children}
</View>
</>
);
};
const styles = StyleSheet.create({
hidden: {
height: 0,
},
list: {
overflow: 'hidden'
},
});
With this, it will fix the Vipul's demo's error: if click accordion so fast, children's opacity descending to 0. and add animation for icon
import {
Animated,
LayoutAnimation,
Platform,
StyleProp,
StyleSheet,
UIManager,
View,
ViewStyle,
} from 'react-native';
import Ionicons from 'react-native-vector-icons/Ionicons;
if (
Platform.OS === 'android' &&
UIManager.setLayoutAnimationEnabledExperimental
) {
UIManager.setLayoutAnimationEnabledExperimental(true);
}
const toggleAnimation = duration => {
return {
duration: duration,
update: {
property: LayoutAnimation.Properties.scaleXY,
type: LayoutAnimation.Types.easeInEaseOut,
},
delete: {
property: LayoutAnimation.Properties.opacity,
type: LayoutAnimation.Types.easeInEaseOut,
},
};
};
interface IAccordion {
title?: JSX.Element | JSX.Element[];
children?: JSX.Element | JSX.Element[];
style?: StyleProp<ViewStyle> | undefined;
}
const Accordion = ({title, children, style}: IAccordion) => {
const [isOpen, setIsOpen] = useState(false);
const animationController = useRef(new Animated.Value(0)).current;
const arrowTransform = animationController.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '90deg'],
});
const onToggle = () => {
setIsOpen(prevState => !prevState);
const duration = 300;
const config = {
duration: duration,
toValue: isOpen ? 0 : 1,
useNativeDriver: true,
};
Animated.timing(animationController, config).start();
LayoutAnimation.configureNext(toggleAnimation(duration));
};
return (
<View style={style ? style : styles.accordion}>
<TouchableOpacity onPress={onToggle} style={styles.heading}>
{title}
<Animated.View style={{transform: [{rotateZ: arrowTransform}]}}>
<Ionicons name={'chevron-forward-outline'} size={18} />
</Animated.View>
</TouchableOpacity>
<View style={[styles.list, !isOpen ? styles.hidden : undefined]}>
{children}
</View>
</View>
);
};
export default Accordion;
I had difficulty using the native API, so I go to third parties. The only thing I couldn't do was make the accordion size automatic.
import { useEffect } from 'react';
import Animated, {
useSharedValue,
useAnimatedStyle,
withTiming,
Easing,
} from 'react-native-reanimated';
import styled from 'styled-components';
const Accordion = ({ children, open, height }) => {
const heightAnimation = useSharedValue(0);
useEffect(() => {
if (open === true) heightAnimation.value = height;
if (open === false) heightAnimation.value = 0;
}, [open]);
const animatedStyle = useAnimatedStyle(() => {
return {
height: withTiming(heightAnimation.value, {
duration: 500,
easing: Easing.bezier(0.25, 0.1, 0.25, 1),
}),
};
});
return (
<>
<Main style={animatedStyle}>{children}</Main>
</>
);
};
const Main = styled(Animated.View)`
width: 100%;
align-items: center;
justify-content: center;
overflow: hidden;
`;
export default Accordion;
Using:
<Accordion height={height} open={open}>
{children}
</Accordion>
As asked here for an example of what I managed to do with it, I tried to get as much out of it as possible.
You can see a deploy here: https://snack.expo.dev/#francisco.ossian/accordion
Libs used, react-native-reanimated

React native button click render different components

I am using Expo React native for my app. I have created three buttons. When user will click the button it will fetch data and render it in one screen. Each button will button will fetch different api. Under the buttons there will be flatlist, where it will display the data. I can do with one button click display the data but I could not figure how to display other buttons api. I share my code in codesandbox. ps: Below code make this app get super slow
This is my code
import React from 'react';
import { Text, View, ScrollView } from 'react-native';
import styled from 'styled-components';
import PressableButton from './Button';
import axios from 'axios';
const api = "https://jsonplaceholder.typicode.com/users";
const anApi = "https://jsonplaceholder.typicode.com/photos"
const Data = ({ data }) => {
return (
<View style={{ flex: 1 }}>
<Text>{JSON.stringify(data, null, 4)}</Text>
</View>
)
}
const AnData = ({ andata }) => {
return (
<View style={{ flex: 1 }}>
<Text>{JSON.stringify(andata, null, 1)}</Text>
</View>
)
}
export default function App() {
const [data, setData] = React.useState([]);
const [anotherdata, setanotherData] = React.useState([]);
const updateState = async () => {
await axios(api)
.then((res) => {
setData(res.data);
})
.catch((err) => {
console.log("failed to catch", err);
});
};
const anoThereState = async () => {
await axios(anApi)
.then((res) => {
setanotherData(res);
})
.catch((err) => {
console.log("failed to catch", err);
});
};
return (
<React.Fragment>
<Container>
<PressableButton onPress={updateState} title='First button' bgColor='#4267B2' />
<PressableButton onPress={anoThereState} title='Second button' bgColor='lightblue' />
<PressableButton onPress={() => true} title='Third button' bgColor='#4267B2' />
</Container>
<Scroll>
{data && data === undefined ? <Text>loading</Text> : <Data data={data} />}
{anotherdata && anotherdata === undefined ? <Text>loading</Text> : <AnData andata={anotherdata} />}
</Scroll>
</React.Fragment>
);
}
const Container = styled.View`
flex-direction: row;
justify-content: center;
padding: 70px 0px 20px 0px;
`;
const Scroll = styled.ScrollView`
flex: 1;
`
Under the buttons there will be flatlist
You're not using FlatList, only showing response in text and the data for second button is huge that's why It's super slow.
Here are the changes I made, check if this is what you're looking for?
Also if you want one data to show at a time you can either use tabs or show/hide the data depending on selection like I've done in code.

Application wide Modal in React Native

I'm currently using react native modal and it serves the purpose of showing modals.
My problem currently is that I want to show the modal application wide. For example when a push notification received I want to invoke the modal regardless of which screen user is in. The current design of the modals bind it to a single screen.
How can this be overcome?
first of all make a context of your modal
const BottomModal = React.createContext();
then provide your modal using reactcontext provider
export const BottomModalProvider = ({children}) => {
const panelRef = useRef();
const _show = useCallback((data, type) => {
panelRef.current.show();
}, []);
const _hide = useCallback(() => {
panelRef.current.hide();
}, []);
const value = useMemo(() => {
return {
_show,
_hide,
};
}, [_hide, _show]);
return (
<BottomPanelContext.Provider value={value}>
{children}
<BottomPanel fixed ref={panelRef} />
</BottomPanelContext.Provider>
);
};
here is code for bottom panel
function BottomPanel(props, ref) {
const {fixed} = props;
const [visible, setVisibility] = useState(false);
const _hide = () => {
!fixed && hideModal();
};
const hideModal = () => {
setVisibility(false);
};
useImperativeHandle(ref, () => ({
show: () => {
setVisibility(true);
},
hide: () => {
hideModal();
},
}));
return (
<Modal
// swipeDirection={["down"]}
hideModalContentWhileAnimating
isVisible={visible}
avoidKeyboard={true}
swipeThreshold={100}
onSwipeComplete={() => _hide()}
onBackButtonPress={() => _hide()}
useNativeDriver={true}
style={{
justifyContent: 'flex-end',
margin: 0,
}}>
<Container style={[{flex: 0.9}]}>
{!fixed ? (
<View style={{flexDirection: 'row', justifyContent: 'flex-end'}}>
<Button
style={{marginBottom: 10}}
color={'white'}
onPress={() => setVisibility(false)}>
OK
</Button>
</View>
) : null}
{props.renderContent && props.renderContent()}
</Container>
</Modal>
);
}
BottomPanel = forwardRef(BottomPanel);
export default BottomPanel;
then wrap your app using the provider
...
<BottomModalProvider>
<NavigationContainer screenProps={screenProps} theme={theme} />
</BottomModalProvider>
...
lastly how to show or hide modal
provide a custom hook
const useBottomPanel = props => {
return useContext(BottomPanelContext);
};
use it anywhere in app like
const {_show, _hide} = useBottomModal();
//....
openModal=()=> {
_show();
}
//...
If you are not using hooks or using class components
you can easily convert hooks with class context
https://reactjs.org/docs/context.html#reactcreatecontext
this way you can achieve only showing the modal from within components
another way is store the panel reference globally anywhere and use that reference to show hide from non-component files like redux or notification cases.