I am using react native deck swipper to display arrays of data.
//ARRAYS OF DATA
const docs = shuffleArray([
{
title: "Austin Wade",
content: 22,
featured_image: require("../../assets/images/beach.jpeg"),
created_at: "2020-11-11T16:26:13.000000Z",
news_url: "https://www.google.com",
key: "caseex6qfO4TPMYyhorner",
},..... more json arrays...])
My problem is, i want to be able to extract the news_url when the card is swipped left, and also use the extracted URL to open expo inapp-browser that will display the webpage e.g www.google.com
I have written a function that opens a web browser.
PLEASE CAN SOMEONE HELP ME
//SWIPPER
<SafeAreaView style={{ flex: 1 }}>
{/* <View style={{ flex: 1, padding: 16 }}> */}
<Swiper
ref={useSwiper}
cards={docs}
cardIndex={0}
backgroundColor="transparent"
stackSize={2}
showSecondCard
cardHorizontalMargin={0}
animateCardOpacity
disableBottomSwipe
renderCard={
((card) => <Cardz card={card} />)
}
onSwiped={(cardIndex) => {
console.log(cardIndex);
}}
onSwipedAll={() => {
console.log("onSwipedAll");
}}
onSwipedTop={() => {
console.log(getLatestNews);
}}
onSwipedBottom={() => {
// <Toast message={success} onDismiss={() => {}} />
}}
//swipping left, opens expo web browser
onSwipedLeft={() => {
_handleWebBrowserAsync(getNewsUrl);
//Alert.alert();
}}
></Swiper>
{/* </View> */}
</SafeAreaView>
);
//WEB BROSWER
//async function to open app inapp web browser
const _handleWebBrowserAsync = async (url) => {
try {
_addLinkingListener();
await WebBrowser.openBrowserAsync(url);
//only calls this method in IOS Devices as it only
//works for IOS Devices
if (Constants.platform.ios) {
_removeLinkingListener();
}
} catch (error) {
Alert.alert("Error:", error.message);;
console.log("Error:" + error.message);
}
};
//CARD COMPONENT
import React from 'react'
import { View, Text, Image, ImageSourcePropType } from 'react-native'
import styles from './Card.styles'
const Cardz = ({ card }) => (
<View activeOpacity={1} style={styles.card}>
<Image
style={styles.image}
source={card.featured_image}
resizeMode="cover"
/>
<View style={styles.photoDescriptionContainer}>
<Text style={styles.text}>{`${card.title}, ${card.content}`}</Text>
</View>
</View>
);
export default Cardz
There are event callbacks.
For example: There is onSwiped prop which is a
function to be called when a card is swiped. it receives the swiped card index.
So, you'll get that index value which you'll use to get the object from the docs array. If the swiped index is 2, you can get the object like this: docs[2].
Related
I'm struggling to understand what causes my component to rerender as there is no state present here. Code to focus on is Flatlist as this is React Native but this is about React and not specifically RN. As you can see inside Flatlist I render renderPost. I know that renderPost, aka reusable component PostListItem rerenders because this console.log you can see inside flatlist gives me repeated keys, and even console logging postsSortedByDate shows me that they render few times. Because of this, I even have a warning child list keys must be unique while I definitely gave flatlist unique IDs from my array, that warning is because of these rerendering. I guess I need to use Memo somewhere?:
const FeedScreen: React.FC<FeedScreenProps> = ({ navigation }) => {
const { postsSortedByDate } = checkPostsContext();
const navigateToCreatePostScreen = async () => {
navigation.navigate("CreatePostScreen");
};
const renderPost: ListRenderItem<IPost> = ({ item }) => (
<PostListItem post={item} key={item.uniquePostID} />
);
return (
<ScreenContainer>
<SafeAreaView>
<Header
containerStyle={styles.containerStyleHeader}
headerTitle="Factory X Feed"
rightIcon="add-photo-alternate"
onPressRightIcon={navigateToCreatePostScreen}
/>
</SafeAreaView>
{postsSortedByDate.length === 0 ? (
<View style={styles.noPostsContainer}>
<Text>
No posts exist at the moment, please create one by tapping on the
image icon in the header. In the meantime, as indicated by the
spinner below, we will continue to try to fetch posts in case some
do exist but are just loading slowly.
</Text>
<ActivityIndicator size="large" color="#0000ff" />
</View>
) : (
<FlatList
data={postsSortedByDate}
renderItem={renderPost}
keyExtractor={(item, index) => {
console.log("IDS:", item.uniquePostID.toString());
//HERE IS THE ISSUE as here I see same keys rendering //multiple times, and I checked and I did not save posts in a wrong way //for few of them to have the same key somehow. Its now about that. //Even logging posts themselves(here) shows me they re render.
return item.uniquePostID.toString();
}}
/>
)}
</ScreenContainer>
);
};
My PostListItem component:
const PostListItem: React.FC<PostItemProps> = ({ post }) => {
return (
<View style={styles.container}>
{post.postImage ? (
<Lightbox
renderContent={() => {
return (
<Image
source={{ uri: post.postImage }}
resizeMode="cover"
style={[
styles.imageInLightbox,
{
width: width,
height: height,
},
]}
/>
);
}}
>
<Image
source={{ uri: post.postImage }}
resizeMode="contain"
style={styles.image}
/>
</Lightbox>
) : null}
<React.Fragment>
{post.postDescription ? (
<Text style={styles.textStyle}>
{hashtagFormatter(post.postDescription)}
</Text>
) : (
<Text style={styles.textStyle}>{defaultPostDescription}</Text>
)}
</React.Fragment>
</View>
);
So why is PostListItem, I mean renderPost rerendering and how to fix it?
I am using https://gorhom.github.io/react-native-bottom-sheet/.
I was wondering how can I open "BottomSheetModal" in a different file e.g Navbar Component.
This is what my code looks like at the moment to open the Bottom Sheet inside of the same component.
const BottomSheetModal: FC = () => {
const bottomSheetModalRef = useRef<BottomSheet>(null);
const snapPoints = useMemo(() => ["25%", "50%"], []);
const handlePresentModalPress = useCallback(() => {
bottomSheetModalRef.current?.present();
}, []);
return (
<>
<Button title="Test" onPress={() => handlePresentModalPress()} />
<BottomSheet
index={1}
style={{ ...shadows.bottomSheet }}
ref={bottomSheetModalRef}
snapPoints={snapPoints}>
<View style={styles.container}>
<Text>Awesome 🎉</Text>
</View>
</BottomSheet>
</>
);
};
So how can I use the opening code of the Bottom Sheet inside of my Navbar Component?
Navbar Component:
// Open BottomSheet here
<TouchableWithoutFeedback onPress={() => openBottomSheet()}>
<View>
<Image
style={styles.avatar}
source={{
uri: "https://lumiere-a.akamaihd.net/v1/images/character_themuppets_kermit_b77a431b.jpeg?region=0%2C0%2C450%2C450",
}}
/>
</View>
</TouchableWithoutFeedback>
Thank you!
I found out to do this, incase anyone comes across this question, I'll post it here!
So what you have to do is pass the ref to the bottom sheet component. So in the Navbar component I created the ref for the bottom sheet, and then passed it into the bottom sheet.
Navbar:
// Create Ref
const userBottomSheetRef = useRef<BottomSheetModal>(null);
// Pass ref into the bottom sheet component
<BottomSheet ref={userBottomSheetRef} snapPoints={["30%"]}/>
Then inside the bottom sheet component you forward the ref using a react function, and then pass it in as normal:
<BottomSheetModal ref={ref} >
<BottomSheetScrollView>
<View style={styles.container}>{children}</View>
</BottomSheetScrollView>
</BottomSheetModal>
I have a list using the ScrollView of react native itself. Basically, I build a list dynamically through an API return.
async fetchData(userSearch) {
const {route} = this.props;
const {params} = route;
const {type} = params;
this.setState({
loading: true,
});
const responseProcedures = await scheduleResource.getProcedures(userSearch);
this.setState({
procedures: responseProcedures.data,
loading: false,
});
}
<ScrollView
onScroll={(event) => this.shouldLoadMoreContent(event)}
>
{procedures.map(procedure => (
<ArrowBox key={procedure.id} onPress={() => RootNavigation.navigate('ProcedureDetails', {procedure})}>
<Text bold style={styles.ProcedureTitle}>
{procedure.name}
</Text>
{!!procedure.synonyms.length && (
<>
<Text bold style={styles.ProcedureSynonymTitle}>
Sinônimos
</Text>
<View style={styles.ProcedureSynonymOptionsContainer}>
{procedure.synonyms.map(synonym => <Text style={styles.ProcedureSynonymOption} key={synonym}>{synonym}</Text>)}
</View>
</>
)}
</ArrowBox>
))}
</ScrollView>
The problem is that I load the entire return from the API and it slows down.
I would like to know how to dynamically load the content and make new calls in the api, when I reach the end of the page.
Api allows me to place offset and limit.
Could someone give me some example?
Thanks!!!!!
Basically the ScrollView is not designed to handle dynamic data, the correct component that is designed to perform this function is called Flatlist. It works almost exactly like ScrollView but it is faster and will only render components that are shown on the screen.
Please import Flatlist from React Native like this...
//At the top of your file, please import FlatList together with all the modules that you want
import { FlatList, Text, View } from "react-native";
Then replace the entire ScrollView in your code with a Flatlist like this:
<FlatList
keyExtractor={(procedure) => procedure.id}
data={this.state.procedures}
renderItem={(procedure) => {
return (
<ArrowBox
key={procedure.id}
onPress={() =>
RootNavigation.navigate("ProcedureDetails", {
procedure })}>
<Text bold style={styles.ProcedureTitle}>
{procedure.name}
</Text>
{!!procedure.synonyms.length && (
<>
<Text bold style={styles.ProcedureSynonymTitle}>
Sinônimos
</Text>
<View
style={styles.ProcedureSynonymOptionsContainer}>
{procedure.synonyms.map((synonym) => (
<Text
style={styles.ProcedureSynonymOption}
key={synonym}>
{synonym}
</Text>
))}
</View>
</>
)}
</ArrowBox>
);
}}
></FlatList>;
I'm using react-native-element for ready-made components in my application and using Overlay to show modals through out the application.
Now the issue is I've a Modal in which I want to show Webview but it doesn't showup on the screen.
My modal code looks like this
export const Modal = ({ visible, showCrossBtn, setModalVisible, backgroundColor, children }) => {
return (
<Overlay
isVisible={visible}
fullScreen={false}
height="auto"
overlayBackgroundColor={backgroundColor}
overlayStyle={styles.overlay}
onBackdropPress={() => setModalVisible(false)}>
<View style={styles.dialogBox}>
{
showCrossBtn && <TouchableOpacity style={[styles.crossPosition, styles.crossStyling]} onPress={() => setModalVisible(false)}>
<Icon name="cross" type="entypo" color={colors.gray} size={30} />
</TouchableOpacity>
}
<View style={styles.body}>
{children}
</View>
</View>
</Overlay>
)
}
In my basic component
export const Home = ({ }) => {
return (
....
....
<Modal
visible={this.props.visible}
setModalVisible={this.props.setModalVisible}
backgroundColor={colors.lightgray}
showCrossBtn={false}
>
<WebView
style={[{ height: 20 }, styles.webView]}
originWhitelist={['*']}
ref={e => webview = e}
source={{ html: '<p>HELLO WORLD</p>'}} />
</Modal>
)
}
Using WebView in modals can be tricky due to the height and width.
To solve the issue, I've created a Snack using react-native-elements Overlay component and WebView. I used some of your code and remove any redundant code for brevity.
Use iOS or Android simulator to run the snack. You can also use your physical device to run the snack using expo app.
Snack - https://snack.expo.io/r1H4FSHML
I have a big code that does not run because of the error in the headline.
This is one file of my code, can anyone see what is wrong? I believe it is the line "{this.state.todos.map((todo,index) =>" (code line 62)
I have capitalised the name of the objects, so that is not the issue (I think).
npm -v 4.6.1
import React, { Component } from 'react';
//import $ from 'jquery';
import { Button, View, FormGroup, FormControl, ControlLabel } from 'react-native';
import { Text } from 'react-native-svg'
/* generating sample data to be shown, these data names are used to access the values*/
var todos = [];
//Will not work first time, since list do not exist in AsyncStorage.
//Get from AsyncStorage.
try{
todos = JSON.parse(AsyncStorage["todos"]);
}catch(ex){
console.log("Not working" + ex);
todos = [];
}
//Errormessage for errorhandeling.
var errorMessage = "";
/*--------------------*/
class Todos extends Component {
constructor(props) {
super(props);
this.state = {
todos
};
this.handleAddTodo = this.handleAddTodo.bind(this);
}
handleAddTodo(todo) {
/* creates todo in list that shows*/
this.setState({todos: [...this.state.todos, todo]});
/*this code saves in AsyncStorage*/
todos.push(todo);
//AsyncStorage...
AsyncStorage.setItem("todos", JSON.stringify(todos));
}
/* function that removes todos from the list*/
handleRemoveTodo(index) {
this.setState({
todos: this.state.todos.filter(function(e, i) {
return i !== index;
})
})
/* now working as expected*/
todos.splice(index, 1);
AsyncStorage.setItem("todos", JSON.stringify(todos));
}
render() {
return (
<View>
<TodosInput onAddTodo={this.handleAddTodo} />
<hr />
<Text>todo count: <span>{this.state.todos.length}</span></Text>
<View>
<View>{this.state.todos.map((todo,index) =>
<View key={index}>
<Text style={style.list-group-item-heading}>{todo.todoTitle}<small><span style={style.label} label-info></span></small> </Text>
<View>{todo.todoDesc}</View>
<Button bsStyle="danger" onClick={this.handleRemoveTodo.bind(this, index)}><span style={style.glyphicon} glyphicon-trash></span></Button>
</View>
)}</View>
</View>
</View>
);
}
It is more likely because of using html tags instead of native components.
Like hr span and small.
So you need to create your own components and also titlecase their names.
Here is possible example how to fix your problem:
const Small = ({ children }) => (
<Text
style={{
fontSize: 10
}}
>
{children}
</Text>
);
const HR = () => (
<View
style={{
height: 1,
borderBottomColor: "black",
borderBottomWidth: 1
}}
/>
);
export default class App extends React.Component {
render() {
return (
<View style={styles.container}>
<View>
<Text>This is span</Text>
</View>
<View>
<Text>
Regular test <Small>With {"<small>"} text</Small> Inside
</Text>
</View>
<View>
<Text>This is {"<HR />"} bellow</Text>
<HR />
</View>
</View>
);
}
}
UPD: Actually there are a lot of other bugs in a code above
Quick list of possible issues:
as pointed dalready - replace all html tags with native components
style={style.list-group-item-heading} You cannot use dash separator in js except ni a strings - possible fix: style={style['list-group-item-heading']}
<View>{todo.todoDesc}</View> - you have to wrao text with Text compnent
<Button /> component have required prop title it should be a string, as well as it dosn't have OnClick handler but requires OnPress
To summarize - i would note that react native uses jsx but it is not html. It have its own API and you'll have to grasp it carefully.