How to open a pdf from my flatlist in react-native? - react-native

Im trying to pick a file (pdf-file) from a module called react-native-file-picker. This works ok, and gaves me name, type, path and uri.
After this, i display the name of the document that i picked in a flatlist.
Now, what i want is to "onPress" of the item in the flatlist, open the document with some pdf viewer or something like that.
I've already tried to use other modules like react-native-view-pdf and react-native-pdf and react-native-pdf-view but i cant access the state of my uri with either of them.
The last one that i used it was react-native-file-viewer and doesn't work very well because it doesn't open the item on press.
This is my actual code.
import React from 'react';
import { StyleSheet, Text, View, TouchableOpacity, Button, TextInput,
Dimensions, FlatList } from 'react-native';
import AsyncStorage from '#react-native-community/async-storage'
import FilePickerManager from 'react-native-file-picker';
import FileViewer from 'react-native-file-viewer';
global.myfunction = function myfunction() {
FilePickerManager.showFilePicker(null, (response) => {
console.log('Response = ', response);
if (response.didCancel) {
console.log('User cancelled file picker');
}
else if (response.error) {
console.log('FilePickerManager Error: ', response.error);
}
else {
this.storeItem(response)
}
});
};
export default class Docs extends React.Component {
static navigationOptions = ({ navigation }) => {
return {
title: 'Docs',
header: null
}
};
state = {
arr: [],
local: '',
password: '',
obj: null,
count: 1,
image: {},
b64: '',
isModalVisible: false,
pdfuri: null,
};
pdf = () => {
FilePickerManager.showFilePicker(null, (response) => {
console.log('Response = ', response);
if (response.didCancel) {
console.log('User cancelled file picker');
}
else if (response.error) {
console.log('FilePickerManager Error: ', response.error);
}
else {
this.storeItem(response)
this.setState({
pdfuri: response.path
});
}
});
}
toggleModal = (item) => {
this.setState({ isModalVisible: !this.state.isModalVisible, obj: item });
};
storeItem(item) {
try {
//we want to wait for the Promise returned by AsyncStorage.setItem()
//to be resolved to the actual value before returning the value~
console.log(item)
var joined = this.state.arr.concat(item);
console.log('files ', joined)
this.setState({ arr: joined })
AsyncStorage.setItem('files', JSON.stringify(joined));
console.log(this.state.arr)
} catch (error) {
console.log(error.message);
}
}
componentDidMount() {
//Here is the Trick
const { navigation } = this.props;
}
componentWillMount() {
AsyncStorage.getItem('files').then(array => {
item = JSON.parse(array)
item ? this.setState({ arr: item }) : null;
console.log(this.state.arr)
})
}
verpdf() {
const path =
"content://com.android.providers.downloads.documents/document/4183"
FileViewer.open(path, { showOpenWithDialog: true })
.then(() => {
// success
})
.catch(error => {
// error
});
}
render() {
return (
<View style={[styles.container, { marginTop: 20 }]}>
<FlatList
data={this.state.arr}
renderItem={({ item }) => <TouchableOpacity onPress=
{this.verpdf(item)} style={{ marginBottom: 10, marginTop: 10, alignItems: 'center' }}>
<Text>{item.fileName}</Text></TouchableOpacity>}
/>
<Button title='ok' onPress={this.pdf}></Button>
</View>
);
}
}
How should i do this?

Try to change the event handler from
onPress=
{this.verpdf(item)}
to
onPress=
{()=>this.verpdf(item)}

Like #Oleg said, to open a certain item i needed to change the event handler to a arrow function.
onPress = {this.verpdf(item)}
to
onPress= {()=>this.verpdf(item)}
After that i wanted to open a certain item from my flatlist which i did:
verpdf(item) {
const path = item.path
FileViewer.open(path, { showOpenWithDialog: true })
.then(() => {
// success
})
.catch(error => {
// error
});
}

Related

react native: Rendering an array of strings and numbers doesn't work

In the react native app I'm retrieving some data from my backend and then want to display it to the app user:
when I log the data I can see that I receive it and stored it properly in the state as an object:
// console.log("received the data: ", this.state.data) ->
received the data: Object {
"a": 48,
"b": "2021-03-29T17:11:51Z",
"c": "",
"d": false
}
But when I try to render that in my view, the screen simply stays empty (no error message):
render() {
// let me check, if the data is really there
Object.entries(this.state.data).map(([key, value]) => {
console.log("key: ", key, "- value: ", value)
})
// output:
// key: a - value: 48,
// key: b - value: 2021-03-29T17:11:51Z,
// key: c - value: ,
// key:d - value: false
return (
<View>
{Object.entries(this.state.data).map(([key, value]) => {
return <View key={key}><Text>{value}</Text></View>
})}
</View>
)
}
I also tried this, but still I receive an empty screen:
render() {
return (
{ this.state.data.map((items, index) => {
return (
<ul key={index}>
{Object.keys(items).map((key) => {
return (
<li key={key + index}>{key}:{items[key]}</li>
)
})}
</ul>
)
})}
)
}
Edit: The full component:
import React from 'react'
import { View, Text } from 'react-native'
import axios from 'axios';
class SuccessScreen extends React.Component {
baseURL = "https://my-backend-server-URL.com/data"
state = {
data: {},
}
componentDidMount() {
this.startGetData();
}
startGetData = () => {
axios.get(this.baseUrl)
.then(response => {
console.log("got the data: ", response.data)
this.state.data = response.data;
})
.catch(function (err) {
//handle error
console.log("error getting data: ", err.message)
return {
type: "REGISTER_USER_FAILED",
payload: null
}
});
};
render() {
console.log("This log will show up")
return (
<View>
{this.state.vehicleData && Object.entries(this.state.vehicleData).map(([key, value]) => {
console.log("this log never shows up... key: ", key) // these logs don't not show up
return <View key={key}><Text>{value}</Text></View>
})}
</View>
)
}
}
export default SuccessScreen;
Wait for data to be defined, Try this:
return (
<View>
{this.state.data && Object.entries(this.state.data).map(([key, value]) => {
return <View key={key}><Text>{value}</Text></View>
})}
</View>
Edit:
Don't mutate this.state.data = response.data that way, use the setState:
this.setState({ data: response.data });
https://reactjs.org/docs/faq-state.html#what-does-setstate-do

How to pass api data from react native to html and vice-versa in react-native-html-to-pdf

i am using react-native-html-to-pdf package to create a pdf file i want to pass the api response from react-native to html and receive the data from html. the options available for this npm package is very minimum
There is no options in the package so that i can use it, can someone help me with this ?
Below is my code.
import React, { Component } from 'react';
import { Text, TouchableOpacity, View, StyleSheet, Image, PermissionsAndroid, Platform,} from 'react-native';
import RNHTMLtoPDF from 'react-native-html-to-pdf';
import htmlContent from './htmlContent'
export default class App extends Component {
constructor(props) {
super(props)
this.state = {
apiData: [],
filePath: ''
}
}
askPermission() {
var that = this;
async function requestExternalWritePermission() {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
{
title: 'External Storage Write Permission',
message:
'App needs access to Storage data in your SD Card ',
}
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
that.createPDF();
} else {
alert('WRITE_EXTERNAL_STORAGE permission denied');
}
} catch (err) {
alert('Write permission err', err);
console.warn(err);
}
}
if (Platform.OS === 'android') {
requestExternalWritePermission();
} else {
this.createPDF();
}
}
componentDidMount(){
fetch(`http://API`)
.then((response) => response.json())
.then((responseJson) => {
**console.log("DATA", responseJson) // NEED TO SEND THIS DATA TO "HTML"**
this.setState(() => ({
apiData: responseJson
}))
})
}
async createPDF() {
let options = {
html:htmlContent, // API DATA SHOULD BE SENT TO HTML
fileName: 'RTT Report',
directory: 'docs',
width: 800,
};
let file = await RNHTMLtoPDF.convert(options);
console.log(file.filePath);
this.setState({filePath:file.filePath});
}
render() {
return (
<View style={styles.MainContainer}>
<TouchableOpacity onPress={this.askPermission.bind(this)}>
<View>
<Image
//We are showing the Image from online
source={{
uri:
'https://raw.githubusercontent.com/AboutReact/sampleresource/master/pdf.png',
}}
//You can also show the image from you project directory like below
//source={require('./Images/facebook.png')}
style={styles.ImageStyle}
/>
<Text style={styles.text}>Create PDF</Text>
</View>
</TouchableOpacity>
<Text style={styles.text}>{this.state.filePath}</Text>
</View>
);
}
}
In createPDF method :
// html:htmlContent, // API DATA SHOULD BE SENT TO HTML
html: this.state.apiData // <-- you have stored your html in the state
EDIT:
Probably I was too fast answering, now I think I got your point , here you have an example :
// html: '<h1>PDF TEST</h1>', <-- example from react-native-html-to-pdf
const exampleData = [
{
title: "Element title",
content: "Element content"
},
{
title: "Other title",
content: "Other element content"
}
]
function generateHTML () {
const data = exampleData
// const data = this.state.apiData // <-- in your case
let htmlContent = '<html><body>'
htmlContent += data.map(entry => {
return `<h5>${entry.title}</h5> <br /> <p>${entry.content}</p>`
}).join(' ')
htmlContent += '</body></html>'
return htmlContent
}

Invariant Violation in React Native: Text strings must be rendered within a <Text> component

I'm working on a React-Native project with REST APis, and I've currently got an invariant violation error. I've experienced this before, but I can't quite figure out what is causing it and how to fix it. If someone could point me in the right direction, I would really appreciate it! The full error is pictured below, and appears to be referencing a number of tags in the code, so I'm unsure exactly where it is originating. Thank you for reading, and thanks in advance!
The code is here:
import React, { Component } from 'react'
import { View, Text, Image, StyleSheet, FlatList} from 'react-native';
import * as Font from 'expo-font';
import styled from 'styled-components';
import dimensions from '../components/ScreenSize';
import colours from '../components/Colours';
import { Audio } from 'expo-av';
import { TouchableHighlight } from 'react-native-gesture-handler';
const client_id = {Client_ID}
const client_secret = {Client_Secret}
const item = ({item}) => (
<View style={{ flex:1, flexDirection: 'column', margin:1}}>
<TouchableHighlight onPress={() => this.fetchTracks(item.id)}>
<View>
<Text>{item.name}</Text>/>
</View>
</TouchableHighlight>
</View>
)
export default class HomeScreen extends React.Component {
state={
fontsLoaded:false,
}
async componentDidMount() {
await Font.loadAsync({
'montserrat-regular': require('../assets/fonts/Montserrat/Montserrat-Regular.ttf'),
'montserrat-light': require('../assets/fonts/Montserrat/Montserrat-Light.ttf'),
'montserrat-semibold': require('../assets/fonts/Montserrat/Montserrat-SemiBold.ttf'),
'montserrat-bold': require('../assets/fonts/Montserrat/Montserrat-Bold.ttf'),
}
).then(() => this.setState({ fontsLoaded:true }))
this.getToken();
this.setAudio();
}
constructor (props) {
super(props)
this.playbackInstance=null;
this.state = {
playing:false,
token: '',
DATA:[],
};
}
setAudio=() => {
Audio.setAudioModeAsync({
allowsRecordingIOS:false,
interruptionModeIOS: Audio.INTERRUPTION_MODE_IOS_DO_NOT_MIX,
playsInSilentModeIOS: true,
shouldDuckAndroid: true,
interruptionModeAndroid: Audio.INTERRUPTION_MODE_ANDROID_DO_NOT_MIX,
playThroughEarpieceAndroid: false,
});
}
componentDidCatch(error, info)
{
console.log(error, info.componentStack);
}
getToken = async() =>
{
try
{
const getspotifytoken = await fetch("https://accounts.spotify.com/api/token",
{
method:'POST',
body: `grant_type=client_credentials&client_id=${client_id}&client_secret=${client_secret}`,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
const spotifytoken = await getspotifytoken.json();
this.setState({
token: spotifytoken.access_token
});
console.log(this.state.token);
}
catch(err)
{
console.log("Error fetching data", err);
}
}
search = async () => {
try
{
console.log("Searching: mood")
const spotifyApiCall = await fetch(`https://api.spotify.com/v1/browse/categories/mood/playlists?`, {
headers: {
Accept: 'application/json',
Authorization: `Bearer ${this.state.token}`,
"Content-Type":'application/json'
}
})
const spotify = await spotifyApiCall.json();
console.log("Items", spotify);
this.setState({
DATA: spotify.playlists.items,
})
}
catch (err)
{
console.log("Error fetching data", err);
}
}
fetchTracks = async (playlistId) => {
console.log('Playlist ', playlistId)
try
{
const getplaylist = await fetch(`https://api.spotify.com/v1.playlist/${playlistId}`,
{
method:'GET',
headers: {
Accept:"application/json",
Authorization:`Bearer ${this.state.token}`,
"Content-Type":"application/json"
}
});
const playlist = await getplaylist.json();
console.log('music ', playlist.tracks.items[0].preview_url);
}
catch (err)
{
console.log("Error fetching data ", err);
}
}
async _loadNewPlaybackInstance(playing, track) {
if(this.playbackInstance != null)
{
await this.playbackInstance.unloadAsync();
this.playbackInstance.setOnPlaybackStatusUpdate(null);
this.playbackInstance = null;
}
const source = {uri: track};
const initialStatus = {
shouldPlay: true,
rate: 1.0,
shouldCorrectPitch: true,
volume: 1.0,
isMuted: false
};
const {sound, status} = await Audio.Sound.createAsync(
source.initialStatus);
this.playbackInstance=sound;
this.playbackInstance.setIsLoopingAsync(false);
this.playbackInstance.playAsync();
if (this.state.selected === playlistId) {
console.log("Playing, so stop");
this.setState({selected:null});
this.playbackInstance.pauseAsync();
return;
}
this.setState({ selected:playlistId});
this._loadNewPlaybackInstance(true, playlist.tracks.items[0].preview_url);
}
render() {
if(!this.state.fontsLoaded ) {
return null
}
return (
<Container>
<Titlebar>
<Title>Music</Title>
</Titlebar>
<HeaderBar2>
<TouchableHighlight onPress={() => this.search()}>
<Header2>Playlists for your Mood</Header2>
</TouchableHighlight>
</HeaderBar2>
<View style={styles.MainContainer}>
{
this.state.DATA.length == 0 &&
<Text style={{padding:10, color:'#D3D3D3'}}/>
}
<FlatList
data = {this.state.DATA}
renderItem={item}
keyExtractor = {item.id}
numColumns={2}
extraData = {this.state}
/>
</View>
</Container>
);
}
}
I think u just have a little typo ..
check this line: <Text>{item.name}</Text>/>
change the last Text to </Text>

How to call a function directly in a Component in React Native

I made a CameraComponent.js having function launchCamera(). I am calling CameraComponent.js in my BottamTab navigation. I had make simple button to launch camera by calling launchCamera(). But i want to launch camera directly when component call in BottamTab navigation just like in whatsapp moving topTab to left. I tried to call function in constructor instead of ComponentWillMount(as it is removed in react native). But nothing work. Here is my below code
export default class CameraComponent extends React.Component {
constructor(props) {
super(props);
launchCamera();
this.state = {
filePath: {},
};
}
launchCamera = () => {
let options = {
storageOptions: {
skipBackup: true,
path: 'images',
},
};
ImagePicker.launchCamera(options, response => {
// console.log('Response = ', response);
if (response.didCancel) {
console.log('User cancelled image picker');
} else if (response.error) {
console.log('ImagePicker Error: ', response.error);
} else {
let source = response;
// You can also display the image using data:
// let source = { uri: 'data:image/jpeg;base64,' + response.data };
this.setState({
filePath: source,
});
}
});
};
render() {
return (
<View style={styles.container}>
<TouchableOpacity onPress={this.launchCamera.bind(this)} >
<Text>Launch Camera</Text>
</TouchableOpacity>
</View>
);
}
}

react native setInterval cannot read property apply

I am new in react native I am trying to render the count of unread notification for that I called my API in HOC it is working fine for initial few seconds but after that, I started to get the below error
func.apply is not a function
below is my code
import React, { Component } from "react";
import PropTypes from "prop-types";
import { Modal, View } from "react-native";
import { themes } from "./constants";
import { AsyncStorage } from "react-native";
export default (OriginalComponent, animationType) =>
class extends Component {
static propTypes = {
handleFail: PropTypes.func,
theme: PropTypes.string,
visible: PropTypes.bool
};
state = {
modalVisible: true
};
static getDerivedStateFromProps({ visible }) {
if (typeof visible === "undefined") {
setInterval(
AsyncStorage.getItem("loginJWT").then(result => {
if (result !== null) {
result = JSON.parse(result);
fetch(serverUrl + "/api/getUnreadNotificationsCount", {
method: "GET",
headers: {
Authorization: "Bearer " + result.data.jwt
}
})
.then(e => e.json())
.then(function(response) {
if (response.status === "1") {
if (response.msg > 0) {
AsyncStorage.setItem(
"unreadNotification",
JSON.stringify(response.msg)
);
} else {
AsyncStorage.setItem("unreadNotification", 0);
}
}
})
.catch(error => {
alert(error);
// console.error(error, "ERRRRRORRR");
});
} else {
AsyncStorage.setItem("unreadNotification", 0);
}
}),
5000
);
return null;
}
return { modalVisible: visible };
}
handleOpenModal = () => {
this.setState({ modalVisible: true });
};
handleCloseModal = () => {
const { handleFail } = this.props;
this.setState({ modalVisible: false }, handleFail);
};
render() {
const { modalVisible } = this.state;
const { theme } = this.props;
return (
<View>
<Modal
animationType={animationType ? animationType : "fade"}
transparent={true}
visible={modalVisible}
onRequestClose={this.handleCloseModal}
>
<View style={themes[theme] ? themes[theme] : themes.transparent}>
<OriginalComponent
handleCloseModal={this.handleCloseModal}
{...this.props}
/>
</View>
</Modal>
</View>
);
}
};
I have not used getDerivedStateFromProps but, according to the docs, it is called on initial component mount and before each render update.
Thus your code is creating a new interval timer on each update without clearing any of the earlier timers, which could be causing a race condition of some sort.
You may want to consider using the simpler alternatives listed in the docs, or at a minimum, insure that you cancel an interval before creating a new one.