I am developing a PDF viewer app.
Problem: I am displaying all the PDF's using Flatlist that is stored in a Firebase database. I am using react-native expo. When I open the application in a browser with expo it works. But when I run it on my phone it shows me an error as given below.
Error: Render Error : Text strings must be rendered within a <Text> Component
import React,{useEffect, useState} from "react";
import { FlatList, Text, StyleSheet, TouchableOpacity, View} from
"react-native";
import { config } from "../firebaseconfig";
import { getDatabase, ref ,onValue } from "firebase/database";
import { initializeApp } from "firebase/app";
const app = initializeApp(config);
function Books({navigation}) {
const [value ,setValue] = useState([]);
function readFunction() {
const db = getDatabase(app);
const reference = ref(db, "Books");
onValue(reference, (snapshot) => {
var main = [];
snapshot.forEach((childSnapshot) => {
const childKey = childSnapshot.key;
const childData = childSnapshot.val();
main.push({
title: childKey,
key:childData
});
});
setValue(main);
})
}
useEffect(() => {
readFunction()
},[]);
function renderItem({item}){
const path = item.key;
return (
<View style={styles.container}>
<View style={styles.square}>
<Text style={styles.h1}>{item.title}</Text>
<TouchableOpacity style={styles.button} onPress={() => navigation.navigate('Document', {path})}>Read</TouchableOpacity>
</View>
</View>
)
};
return (
<FlatList
data={value}
renderItem={renderItem}
keyExtractor={item => item.key}
/>
)
}
const styles = StyleSheet.create({
container:{
display: 'flex',
paddingLeft: '30px',
paddingRight:'30px',
paddingTop:'20px',
paddingBottom:'10px'
},
square:{
backgroundColor: 'white',
borderRadius: 4,
padding: '30px',
shadowColor: 'black',
shadowRadius: 10,
shadowOpacity: 1,
},
h1:{
textAlign: 'left',
fontFamily: 'Merriweather',
fontSize: 30,
},
p: {
textAlign: 'justify',
fontFamily: 'Open Sans',
fontSize: 15,
color: '#C8C8C8',
lineHeight: 18,
},
button: {
backgroundColor: '#3EDD84',
color: 'white',
width: '90px',
borderRadius: 3,
textAlign: 'center',
display: 'flex',
marginTop: '15px',
marginRight: '70px',
lineHeight:30,
fontSize: 25,
fontFamily: 'merriweather',
}
});
export default Books;
Try Doing this same
<View style={styles.container}>
<View style={styles.square}>
<Text style={styles.h1}>{item.title}</Text>
<TouchableOpacity style={styles.button} onPress={() => navigation.navigate('Document', {path})}>
<Text>Read</Text>
</TouchableOpacity>
</View>
You can't add Text inside TouchableOpacity component
Do this:
<TouchableOpacity style={styles.button} onPress={() => navigation.navigate('Document', {path})}>
<Text>Read</Text>
</TouchableOpacity>
I think the word Read between the TouchableOpacity tags should be within Text tags.
Related
I am about to making an app with React Native and I have three screens with deferent styles (themes). I want to navigate among these three screens, So I am passing data from my main screen (App.js) as parent to the other three as child (screen1, screen2, screen3). I used a modal which has three button in it and i want whenever I pressed one of them, the screen change to the pressed one. This is my App.js file
import React, { useState, useEffect } from "react";
import { StyleSheet, View } from "react-native";
import Screen1 from "./screens/CalcScreen";
import Screen2 from "./screens/Screen1";
import Screen3 from "./screens/Screen2";
export default function App() {
const [whiteScreen, setWhiteScreen] = useState(false);
const [darkScreen, setDarkScreen] = useState(false);
const [pinkScreen, setPinkScreen] = useState(false);
const whiteScreenHandler = () => {
setWhiteScreen(true);
setDarkScreen(false);
setPinkScreen(false);
};
const darkScreenHandler = () => {
setDarkScreen(true);
setWhiteScreen(false);
setPinkScreen(false);
};
const pinkScreenHander = () => {
setPinkScreen(true);
setWhiteScreen(false);
setDarkScreen(false);
};
let content = <screen1 setDarkScreen={darkScreenHandler} />;
let content2 = <Screen2 setWhiteScreen={whiteScreenHandler} />;
let content3 = <Screen3 setPinkScreen={pinkScreenHander} />;
if (darkScreen) {
content = content;
} else if (whiteScreen) {
content = content2;
} else if (pinkScreen) {
content = content3;
}
return <View style={styles.container}>{content}</View>;
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#374351",
},
});
And this is one of my screens and my modals in my screens, the other two screens are same in coding but not in styles and I used this modal in each three screens,(does it right to use in three screens?) anyway whenever I'm pressing modal's button to change the screens i got props.screen handler() is not a function. What is wrong in my code?
import React, { useState } from "react";
import {
StyleSheet,
Text,
ScrollView,
View,
TouchableOpacity,
Alert,
Animated,
Modal,
Pressable,
} from "react-native";
const Screen1 = (props) => {
return (
<View style={styles.screen}>
<Modal
animationType="slide"
transparent={true}
visible={modalVisible}
onRequestClose={() => {
setModalVisible(!modalVisible);
}}
>
<View style={styles.centeredView}>
<View style={styles.modalView}>
<View style={styles.button}></View>
<Text style={styles.modalText}>Themes</Text>
<TouchableOpacity
style={styles.darkTheme}
onPress={() => {
props.setDarkScreen();
}}
>
<Text>Dark</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.whiteTheme}
onPress={() => {
props.setWhiteScreen();
}}
>
<Text>White</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.pinkTheme}
onPress={() => {
props.setPinkScreen();
}}
>
<Text>Pink</Text>
</TouchableOpacity>
<Pressable
style={[styles.buttonClose]}
onPress={() => setModalVisible(!modalVisible)}
>
<Text style={styles.textStyle}>X</Text>
</Pressable>
</View>
</View>
</Modal>
<Pressable
style={[styles.button, styles.buttonOpen]}
onPress={() => setModalVisible(true)}
>
<Text style={styles.openText}>{">"}</Text>
</Pressable>
</View>
)
}
const styles = StyleSheet.create({
screen: {
backgroundColor: "#fafcff",
},
centeredView: {
// flex: 1,
marginStart: 15,
justifyContent: "center",
alignItems: "center",
// marginTop: 70,
},
modalView: {
marginTop: 200,
backgroundColor: "rgba(255,255,255,0.5)",
borderRadius: 20,
padding: 20,
alignItems: "center",
shadowColor: "#000",
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.5,
shadowRadius: 4,
elevation: 5,
},
button: {
borderRadius: 15,
padding: 10,
elevation: 2,
justifyContent: "center",
alignItems: "center",
marginTop: 20,
},
buttonOpen: {
backgroundColor: "rgba(1,143,132,0.6)",
position: "absolute",
marginTop: 45,
marginStart: -25,
elevation: 2,
height: 70,
width: 40,
},
buttonClose: {
backgroundColor: "rgba(1,1,1,1)",
height: 40,
width: 40,
borderRadius: 50,
// padding: 10,
elevation: 2,
justifyContent: "center",
alignItems: "center",
marginTop: 20,
},
textStyle: {
color: "white",
// fontWeight: "bold",
textAlign: "center",
},
modalText: {
marginBottom: 10,
textAlign: "center",
},
openText: {
fontSize: 25,
fontWeight: "bold",
color: "white",
textAlign: "right",
},
});
export default Screen1;
You can easily doing that by using "React Native Navigation". Here is the documentation:
https://reactnavigation.org/docs/getting-started. I also recommend you that you might want to create your screens into different files to make your code cleaner.
When I click the RoundedButton the TouchableOpacity works i.e the opacity of the button reduces but the onPress function doesn't work, the data being passed to the onPress function is correct(as given in the code below). Also, when I tried to console.log("something") inside the onPress function, it doesn't get printed in the console of my terminal.
Here I have the code with function component.
Focus.js file
import React, { useState } from "react";
import { Text, View, StyleSheet } from "react-native";
import { TextInput } from "react-native-paper";
import { RoundedButton } from "../../component/RoundedButton";
export const Focus = ({ addSubject }) => {
const [tmpItem, setTmpItem] = useState();
return (
<View style={styles.container}>
<View style={styles.titleContainer}>
<Text style={styles.title}>What would you like to focus on?</Text>
<View style={styles.inputContainer}>
<TextInput
style={{ flex: 1, marginRight: 20 }}
onSubmitEditing={({ nativeEvent }) => {
setTmpItem(nativeEvent.text);
console.log("tmpItem value set " + tmpItem);
}}
/>
<RoundedButton
size={50}
title="+"
onPress={() => {
console.log("value passed!");
addSubject(tmpItem);
}}
/>
</View>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
titleContainer: {
flex: 0.5,
padding: 10,
justifyContent: "center",
},
title: {
color: "white",
fontWeight: "bold",
fontSize: 21,
},
inputContainer: {
paddingTop: 10,
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
},
});
RoundedButton.js File
import React from "react";
import { TouchableOpacity, Text, StyleSheet } from "react-native";
export const RoundedButton = ({
style = {},
textStyle = {},
size = 125,
...props
}) => {
return (
<TouchableOpacity style={[styles(size).radius, style]}>
<Text style={[styles.text, textStyle]}>{props.title}</Text>
</TouchableOpacity>
);
};
const styles = (size) =>
StyleSheet.create({
radius: {
borderRadius: size / 2,
width: size,
height: size,
alignItems: "center",
justifyContent: "center",
borderColor: "white",
borderWidth: 2,
},
text: {
color: "white",
fontSize: size / 3,
},
});
App.js file
import React, { useState } from "react";
import { Text, View, StyleSheet } from "react-native";
import { Focus } from "./src/features/focus/Focus";
export default function App() {
const [focusSubject, setFocusSubject] = useState(null);
return (
<View style={styles.container}>
{focusSubject ? (
<Text style={{ flex: 1, color: "white", fontSize: 30 }}>
Here is where I am going to build a timer
</Text>
) : (
<Focus addSubject={setFocusSubject} />
)}
<Text style={{ flex: 1, color: "white", fontSize: 30 }}>
{focusSubject}
</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 50,
backgroundColor: "#252250",
},
});
you need to extract your onPress props in RoundButton.js and pass to your TouchableOpacity
export const RoundedButton = ({
style = {},
textStyle = {},
size = 125,
onPress,//<===this
...props
}) => {
return (
<TouchableOpacity onPress={onPress} style={[styles(size).radius, style]}>
<Text style={[styles.text, textStyle]}>{props.title}</Text>
</TouchableOpacity>
);
};
it's seem that you forget to pass onPress function to TouchOpacity
export const RoundedButton = ({
style = {},
textStyle = {},
size = 125,
onPress,
...props
}) => {
return (
<TouchableOpacity onPress={onPress} style={[styles(size).radius, style]}>
<Text style={[styles.text, textStyle]}>{props.title}</Text>
</TouchableOpacity>
);
};
Now It's Working
Focus.js file
import React, { useState } from "react";
import { Text, View, StyleSheet } from "react-native";
import { TextInput } from "react-native-paper";
import { RoundedButton } from "../../component/RoundedButton";
export const Focus = ({ addSubject }) => {
const [tmpItem, setTmpItem] = useState();
return (
<View style={styles.container}>
<View style={styles.titleContainer}>
<Text style={styles.title}>What would you like to focus on?</Text>
<View style={styles.inputContainer}>
<TextInput
style={{ flex: 1, marginRight: 20 }}
onSubmitEditing={({ nativeEvent }) => {
setTmpItem(nativeEvent.text);
console.log("tmpItem value set " + tmpItem);
}}
/>
<RoundedButton
size={50}
title="+"
onPress={() => {
console.log("value passed!");
addSubject(tmpItem);
}}
/>
</View>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
},
titleContainer: {
flex: 0.5,
padding: 10,
justifyContent: "center",
},
title: {
color: "white",
fontWeight: "bold",
fontSize: 21,
},
inputContainer: {
paddingTop: 10,
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
},
});
RoundedButton.js File
import React from "react";
import { TouchableOpacity, Text, StyleSheet } from "react-native";
export const RoundedButton = ({
style = {},
textStyle = {},
size = 125,
onPress,
...props
}) => {
return (
<TouchableOpacity onPress={onPress} style={[styles(size).radius, style]}>
<Text style={[styles.text, textStyle]}>{props.title}</Text>
</TouchableOpacity>
);
};
const styles = (size) =>
StyleSheet.create({
radius: {
borderRadius: size / 2,
width: size,
height: size,
alignItems: "center",
justifyContent: "center",
borderColor: "white",
borderWidth: 2,
},
text: {
color: "white",
fontSize: size / 3,
},
});
App.js file
import React, { useState } from "react";
import { Text, View, StyleSheet } from "react-native";
import { Focus } from "./src/features/focus/Focus";
export default function App() {
const [focusSubject, setFocusSubject] = useState();
return (
<View style={styles.container}>
{focusSubject ? (
<Text style={{ flex: 1, color: "white", fontSize: 30 }}>
Here is where I am going to build a timer
</Text>
) : (
<Focus addSubject={setFocusSubject} />
)}
<Text style={{ flex: 1, color: "white", fontSize: 30 }}>
{focusSubject}
</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 50,
backgroundColor: "#252250",
},
});
I have a functional component Comment, which dynamically receives data to show in a socialmedia like application. If a long text is given as a comment, the height of the card is increased accordinly but the last line is cropped by a half. I tried with many solutions I found in other posts, like textBreakStrategy={'simple'} or flexWrap: 'wrap' in the container, but nothing is solving this issue.
As additional information, a lot of Comments are being rendered in a container ScrollView, and the ScrollView is inside a View with flex: 1. I dont think there's any problem with the container.
To give a better idea, here's a picture of a Comment with a long text getting cropped in the last line:
Here is the component's code:
import React, { useState } from 'react';
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
import AntDesign from 'react-native-vector-icons/AntDesign';
import { formatDistanceToNow } from 'date-fns'
import { es } from 'date-fns/esm/locale'
const Comment = props => {
const amITheCreator = (comment, user) => {
if (comment && user) {
return comment.creator_id === user.id;
}
return false;
};
const user = props.user;
const comment = props.item;
const date = new Date(comment.createdAt);
const relativeDate = formatDistanceToNow(date, { addSuffix: true, locale: es });
const itsMine = amITheCreator(comment, user);
const [liked, setLiked] = useState(comment.liked);
const [likes, setLikes] = useState(comment.likes);
const toggleLike = () => {
if (liked) {
setLikes(likes - 1);
} else {
setLikes(likes + 1);
}
setLiked(!liked);
};
const onLike = () => {
toggleLike();
props.onLike(comment.id);
};
return (
<View style={{ ...styles.container, backgroundColor: itsMine ? '#0095ff' : 'white' }}>
<View style={styles.header}>
<Text style={{ ...styles.owner, color: itsMine ? 'white' : '#222b45' }}>{comment.name}</Text>
<Text style={{ ...styles.date, color: itsMine ? 'white' : '#8f9bb3' }}>{relativeDate}</Text>
</View>
<View style={styles.contentContainer}>
<Text style={{ ...styles.content, color: itsMine ? 'white' : '#222b45' }}>{comment.content}</Text>
</View>
<View style={styles.header}>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<AntDesign name={'heart'} color={itsMine ? 'white' : '#8f9bb3'} size={14} />
<Text style={{ ...styles.likes, color: itsMine ? 'white' : '#8f9bb3' }}> {likes ? likes : '0'} Me gusta</Text>
</View>
<TouchableOpacity onPress={onLike}>
{liked ?
<AntDesign name={'heart'} color={'#ff2d55'} size={20} />
:
<AntDesign name={'hearto'} color={itsMine ? 'white' : '#8f9bb3'} size={20} />
}
</TouchableOpacity>
</View>
</View >
);
};
const styles = StyleSheet.create({
container: {
marginHorizontal: 15,
marginBottom: 15,
borderRadius: 7,
padding: 10,
},
header: {
flexDirection: 'row',
justifyContent: 'space-between',
},
owner: {
fontWeight: 'bold',
},
date: {
fontSize: 12,
color: 'white',
},
contentContainer: {
padding: 5,
flexWrap: 'wrap',
},
content: {
fontSize: 16,
alignSelf: 'stretch',
width: '100%',
padding: 5,
},
likes: {
color: 'white',
fontSize: 15,
fontWeight: 'bold',
},
});
export default Comment;
I am using the react-native-autocomplete-input package for auto-complete searching.
https://www.npmjs.com/package/react-native-autocomplete-input
Here is a template code which works:
//Example of React Native AutoComplete Input
//https://aboutreact.com/example-of-react-native-autocomplete-input/
//import React in our code
import React, {useState, useEffect} from 'react';
//import all the components we are going to use
import {
SafeAreaView,
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native';
//import Autocomplete component
import Autocomplete from 'react-native-autocomplete-input';
const App = () => {
const [films, setFilms] = useState([]); // For the main data
const [filteredFilms, setFilteredFilms] = useState([]); // Filtered data
const [selectedValue, setSelectedValue] = useState({}); // selected data
useEffect(() => {
fetch('https://aboutreact.herokuapp.com/getpost.php?offset=1')
.then((res) => res.json())
.then((json) => {
const {results: films} = json;
setFilms(films);
//setting the data in the films state
})
.catch((e) => {
alert(e);
});
}, []);
const findFilm = (query) => {
//method called everytime when we change the value of the input
if (query) {
//making a case insensitive regular expression
const regex = new RegExp(`${query.trim()}`, 'i');
//setting the filtered film array according the query
setFilteredFilms(
films.filter((film) => film.title.search(regex) >= 0)
);
} else {
//if the query is null then return blank
setFilteredFilms([]);
}
};
return (
<SafeAreaView style={{flex: 1}}>
<View style={styles.container}>
<Autocomplete
autoCapitalize="none"
autoCorrect={false}
containerStyle={styles.autocompleteContainer}
//data to show in suggestion
data={filteredFilms}
//default value if you want to set something in input
defaultValue={
JSON.stringify(selectedValue) === '{}' ?
'' :
selectedValue.title
}
// onchange of the text changing the state of the query
// which will trigger the findFilm method
// to show the suggestions
onChangeText={(text) => findFilm(text)}
placeholder="Enter the film title"
renderItem={({item}) => (
//you can change the view you want to show in suggestions
<TouchableOpacity
onPress={() => {
setSelectedValue(item);
setFilteredFilms([]);
}}>
<Text style={styles.itemText}>
{item.title}
</Text>
</TouchableOpacity>
)}
/>
<View style={styles.descriptionContainer}>
{films.length > 0 ? (
<>
<Text style={styles.infoText}>
Selected Data
</Text>
<Text style={styles.infoText}>
{JSON.stringify(selectedValue)}
</Text>
</>
) : (
<Text style={styles.infoText}>
Enter The Film Title
</Text>
)}
</View>
</View>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
backgroundColor: '#F5FCFF',
flex: 1,
padding: 16,
marginTop: 40,
},
autocompleteContainer: {
backgroundColor: '#ffffff',
borderWidth: 0,
},
descriptionContainer: {
flex: 1,
justifyContent: 'center',
},
itemText: {
fontSize: 15,
paddingTop: 5,
paddingBottom: 5,
margin: 2,
},
infoText: {
textAlign: 'center',
fontSize: 16,
},
});
export default App;
The input box looks like below.
I want a search icon at the right/left inside of the search input box.
Is there any way I can do this?
Here is the working code with
//Example of React Native AutoComplete Input
//https://aboutreact.com/example-of-react-native-autocomplete-input/
//import React in our code
import React, {useState, useEffect} from 'react';
import AntDesign from 'react-native-vector-icons/AntDesign';
//import all the components we are going to use
import {
SafeAreaView,
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native';
//import Autocomplete component
import Autocomplete from 'react-native-autocomplete-input';
const App = () => {
const [films, setFilms] = useState([]); // For the main data
const [filteredFilms, setFilteredFilms] = useState([]); // Filtered data
const [selectedValue, setSelectedValue] = useState({}); // selected data
useEffect(() => {
fetch('https://aboutreact.herokuapp.com/getpost.php?offset=1')
.then((res) => res.json())
.then((json) => {
const {results: films} = json;
setFilms(films);
//setting the data in the films state
})
.catch((e) => {
alert(e);
});
}, []);
const findFilm = (query) => {
//method called everytime when we change the value of the input
if (query) {
//making a case insensitive regular expression
const regex = new RegExp(`${query.trim()}`, 'i');
//setting the filtered film array according the query
setFilteredFilms(
films.filter((film) => film.title.search(regex) >= 0)
);
} else {
//if the query is null then return blank
setFilteredFilms([]);
}
};
return (
<View style={{flex: 1}}><View><Text>Hello Friend</Text></View>
<View style={styles.container}>
<View style={styles.searchSection}>
<AntDesign
name="search1"
size={18}
color="gray"
style={styles.searchIcon}
/>
<Autocomplete
autoCapitalize="none"
autoCorrect={false}
containerStyle={styles.autocompleteContainer}
inputContainerStyle={styles.inputContainer}
//data to show in suggestion
data={filteredFilms}
//default value if you want to set something in input
defaultValue={
JSON.stringify(selectedValue) === '{}'
? ''
: selectedValue.title + selectedValue.id
}
// onchange of the text changing the state of the query
// which will trigger the findFilm method
// to show the suggestions
onChangeText={(text) => findFilm(text)}
placeholder="Search doctors, specialities, symptoms"
renderItem={({item}) => (
//you can change the view you want to show in suggestions
<View>
<TouchableOpacity
onPress={() => {
setSelectedValue(item);
setFilteredFilms([]);
}}>
<Text style={styles.itemText}>{item.title + item.id}</Text>
</TouchableOpacity>
</View>
)}
/>
<AntDesign
name="close"
size={18}
color="gray"
style={styles.clearIcon}
/>
</View>
<View style={styles.descriptionContainer}>
{films.length > 0 ? (
<>
<Text style={styles.infoText}>
Selected Data
</Text>
<Text style={styles.infoText}>
{JSON.stringify(selectedValue)}
</Text>
</>
) : (
<Text style={styles.infoText}>
Enter The Film Title
</Text>
)}
</View>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
backgroundColor: '#F5FCFF',
flex: 1,
padding: 16,
marginTop: 40,
},
autocompleteContainer: {
backgroundColor: '#ffffff',
borderWidth: 0,
marginLeft: 10,
marginRight: 10,
//paddingLeft: 15,
},
inputContainer: {
//minWidth: 300,
//width: "90%",
//height: 55,
backgroundColor: 'transparent',
//color: '#6C6363',
//fontSize: 18,
//fontFamily: 'Roboto',
borderBottomWidth: 1,
//borderBottomColor: 'rgba(108, 99, 99, .7)',
borderColor: 'transparent',
},
descriptionContainer: {
flex: 1,
justifyContent: 'center',
},
itemText: {
fontSize: 15,
paddingTop: 5,
paddingBottom: 5,
margin: 2,
},
infoText: {
textAlign: 'center',
fontSize: 16,
},
// testing below
searchSection: {
flex: 1,
height: 50,
borderRadius: 10,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
marginLeft: '5%',
marginRight: '5%',
backgroundColor: '#fff',
},
searchIcon: {
//padding: 10,
paddingLeft: 10,
backgroundColor: 'transparent',
},
clearIcon: {
paddingRight: 10,
backgroundColor: 'transparent',
},
});
export default App;
npm install react-native-vector-icons for the AntDesign icons.
I am using "react-native-vector-icons": "^7.1.0".
Your output will be like:
Have a great day!!
I'm new here and in react-native.
I want render some data from IMDB Api with the "hook" method.
So I try to render data in a flatlist, I can see in the console that the data is retrieved properly in a jSon format witch is a good thing but when I open my app I don't see any rendering on the screen.
I try to use the 'Hook' method and not the 'classes' and I'm pretty sure that I don't use properly the useState method.
So any help or advices are welcome.
My code:
App
import React, { useState } from "react";
import {
View,
Text,
StyleSheet,
Button,
TextInput,
FlatList,
} from "react-native";
// import films from "../Helpers/filmsData";
import FilmsItem from "./FilmsItem";
import getFilmsFromApiWithSearchedText from "../Api/TMDBApi";
const Search = () => {
const [films, setFilms] = useState([]);
const _loadFilms = () => {
getFilmsFromApiWithSearchedText("White").then(
(data) => setFilms({ film: data.results }),
console.log(films)
);
};
return (
<View style={styles.container}>
<TextInput style={styles.input} placeholder="Titre du film" />
<Button
style={styles.button}
title="Rechercher"
onPress={() => _loadFilms()}
/>
<FlatList
data={setFilms.films}
keyExtractor={(item) => item.id.toString()}
renderItem={({ item }) => <FilmsItem films={item} />}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 20,
},
input: {
marginLeft: 5,
marginRight: 5,
height: 50,
paddingLeft: 5,
borderWidth: 1,
borderColor: "#000000",
},
});
export default Search;
Api request:
// Api/TMDBApi.js
const API_TOKEN = "**************************";
const getFilmsFromApiWithSearchedText = (text) => {
const url =
"https://api.themoviedb.org/3/search/movie?api_key=" +
API_TOKEN +
"&language=en&query=" +
text;
return fetch(url)
.then((response) => response.json())
.catch((error) => console.error(error));
};
export default getFilmsFromApiWithSearchedText;
FilmItem:
import React from "react";
import { View, Text, StyleSheet, Image } from "react-native";
const FilmsItem = (props) => {
const film = props.film;
return (
<View style={styles.container}>
<Image style={styles.img} source={{ uri: "image" }} />
<View style={styles.content}>
<View style={styles.header}>
<Text style={styles.title}>{film.title}</Text>
<Text style={styles.vote}>{film.vote_average}</Text>
</View>
<View style={styles.synopsis}>
<Text style={styles.synopsysText} numberOfLines={6}>
{film.overview}
</Text>
</View>
<View style={styles.date}>
<Text style={styles.dateText}>Sorti le {film.release_date}</Text>
</View>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flexDirection: "row",
height: 190,
},
content: {
flex: 1,
margin: 5,
},
img: {
height: 180,
width: 120,
margin: 5,
backgroundColor: "gray",
},
header: {
flex: 3,
flexDirection: "row",
},
title: {
flex: 1,
flexWrap: "wrap",
fontWeight: "bold",
fontSize: 20,
paddingRight: 5,
},
vote: {
fontWeight: "bold",
fontSize: 20,
color: "#666666",
},
synopsis: {
flex: 7,
},
synopsysText: {
fontStyle: "italic",
color: "#666666",
},
date: {
flex: 1,
paddingBottom: 10,
},
dateText: {
textAlign: "right",
fontSize: 14,
},
});
export default FilmsItem;
const _loadFilms = () => {
getFilmsFromApiWithSearchedText("White").then(
(data) => setFilms({ film: data.results }),
console.log(films)
);
};
setFilms({ film: data.results }) will set an object, but you need an array in there.
Try to do something like this: setFilms(data.results)
use useState in this way
const _loadFilms = () => {
getFilmsFromApiWithSearchedText("White").then(
(data) => setFilms(data.results),
console.log(films)
);
};
then in Flatlist you can use like this
return (
<View style={styles.container}>
<TextInput style={styles.input} placeholder="Titre du film" />
<Button
style={styles.button}
title="Rechercher"
onPress={() => _loadFilms()}
/>
<FlatList
data={films}
keyExtractor={(item) => item.id.toString()}
renderItem={({ item }) => <FilmsItem films={item} />}
/>
</View>
);