pass dynamic URLs to require() - android

I need to loop through my products array then pass the data as props but for my image urls I cant do that and console show me the following:
invalid call at line 21 require(this.props.img)
here is my code:
app.js
export default class App extends React.Component {
state = {
products: [
{
id: 1,
details: 'this is a macbook',
image: '../Images/macbook.jpg',
price: '1000$',
},
{
id: 2,
details: 'this is a PS4 pro',
image: '../Images/ps4pro.jpeg',
price: '500$',
},
{
id: 3,
details: 'this is a beats',
image: '../Images/beats.jpeg',
price: '200$',
},
]
}
showProds = () => {
let prods = [];
for (let i = 0; i <= this.state.products.length - 1; i++) {
prods.push(<Product details={this.state.products[i].details} img={this.state.products[i].image} />)
}
console.log(prods);
return prods;
}
render() {
return <ScrollView>
{this.showProds()}
</ScrollView>
}
}
product component
export default class Product extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<View style={styles.card}>
<View style={styles.prod}>
<Text style={styles.prod_details}>{this.props.details}</Text>
<Image style={styles.img} source={require(this.props.img)} />
</View>
<View style={styles.btn}>
<TouchableOpacity>
<Text style={styles.btn_text}>Delete</Text>
</TouchableOpacity>
</View>
</View >
)
}
}
I'm new to react-native. I also tried to use require in my loop but no luck.
any help would be appreciated.

Change your products.image so their will be in this format:
products: {
...
image: require('.../Images.image.jpeg'),
...
}
And then change image source to this:
<Image style={styles.img} source={this.props.img} />

You cant dynamically require images in react-native. It's a major flaw in react native.
You have two options,
1.Either host the images onany server like cloudinary and send the URI
2.You can require all images at start, and then just include those image components like :
const flower = () => (
<Image source={require('../../)} />
)
const rat = () => (
<Image source={require('../../)} />
)
and (this.props.img == 'flower') ? flower():rat()
Hope you get the gyst. Feel free for doubts

Related

How can I send images/video and voice messages in React-native-Gifted-chat using render actions?

I want to send images/video in chat application, developed using React-native-gifted-chat and Firebase, How can I create action for them and call that actions to upload in firebase and send images/video?
Here is my code.
handleSendImage = () => {
console.log("handleSendImage");
};
renderActions(props) {
return (
<Actions
{...props}
// containerStyle={{
// width: 70,
// }}
icon={() => (
<Icon
name={"camera"}
size={30}
color={colors.btnBg}
font={"FontAwesome"}
onPress={this.handleSendImage}
/>
)}
onSend={(args) => console.log(args)}
/>
);
}
<GiftedChat
placeholder={"Hey!"}
alwaysShowSend
messages={messages}
onSend={(newMessage) => this.onSend(this.chatID(), newMessage)}
renderInputToolbar={this.renderInputToolbar}
renderActions={this.renderActions}
user={{
_id: senderId,
name: senderName,
}}
/>
How can I click on particular actions and send voice and images/video respectively?
Gifted chat has renderActions property itself so just need to create custom action to upload image/video and voice.
Here, I am attaching code for upload documents like PDF/Doc file.
To upload image/video you just need to change that package instead of I've used document-picker
const renderActions = (props) => {
return (
<Actions
{...props}
options={{
['Document']: async (props) => {
try {
const result = await DocumentPicker.pick({
type: [DocumentPicker.types.pdf],
});
console.log("image file",result)
} catch (e) {
if (DocumentPicker.isCancel(e)) {
console.log("User cancelled!")
} else {
throw e;
}
}
},
Cancel: (props) => { console.log("Cancel") }
}}
onSend={args => console.log(args)}
/>
)
};
Gifted-chat component
<GiftedChat
messages={messages}
showAvatarForEveryMessage={true}
onSend={messages => onSend(messages)}
renderActions={() => renderActions()}
user={{
_id: 2,
name: 'React Native',
avatar: 'https://placeimg.com/140/140/any',
}}
renderCustomView={renderCustomView}
/>

Event from event.nativeEvent.data logs as setImmediate$0.4162155499933975$1

I am trying to pass token and other user details from React Web application to React Native application with Webview (react-native).
When I do
window.postMessage(JSON.stringify(reactNativeObj), '*');
and log the event in React native application with
console.log( "On Message", event.nativeEvent.data );
It is printing the log as
On Message: setImmediate$0.4162155499933975$1
It should print my object instead.
I have tried almost everything and re-read the document https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage but couldn't make it work.
Any help would be appreciated.
If you're using RN >= 0.60 then you're using react-native-webview >= 8.0.0, which you'll have to use window.ReactNativeWebView.postMessage.
If you have access and can edit the website code, then you should add a postMessage call like this:
if (window.ReactNativeWebView) {
window.ReactNativeWebView.postMessage(JSON.stringify(reactNativeObj));
}
If you don't have access to the website code or if you don't want to alter its' contents, then you can inject some code that modifies window.postMessage to use window.ReactNativeWebView.postMessage along with the default method:
const WebView_Injection_postMessage = `
(function() {
window.default_postMessage = window.postMessage;
window.postMessage = function(data, domain) {
window.default_postMessage(data, domain);
if (window.ReactNativeWebView) {
window.ReactNativeWebView.postMessage(data);
}
}
})();
`;
const App: () => React$Node = () => {
const [result, setResult] = useState('');
function onWebViewMessage(event) {
console.log('onWebViewMessage', event.nativeEvent.data);
setResult(JSON.stringify(JSON.parse(event.nativeEvent.data), null, 2));
}
return (
<>
<StatusBar barStyle="dark-content" />
<SafeAreaView style={styles.container}>
<View style={styles.webViewContainer}>
<WebView
source={{ uri: 'https://zikro.gr/dbg/so/60052061/react' }}
style={styles.webWiew}
injectedJavaScript={WebView_Injection_postMessage}
onMessage={onWebViewMessage}
/>
</View>
<View style={styles.resultView}>
<Text style={styles.resultText}>{result}</Text>
</View>
</SafeAreaView>
</>
);
};
`;
The web app at https://zikro.gr/dbg/so/60052061/react is a React 16 development app with the following code:
<script src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/#babel/standalone/babel.min.js"></script>
<script type="text/babel">
class App extends React.Component {
postMessage = () => {
console.log('Posting message...');
const reactNativeObj = {
location: window.location.href,
title: document.title,
dt: (new Date).toISOString()
}
window.postMessage(JSON.stringify(reactNativeObj), '*');
}
render() {
return (
<button onClick={this.postMessage} id="post-message">Post Message</button>
)
};
}
ReactDOM.render(<App/>, document.getElementById('main'));
</script>
Last but not least, window.ReactNativeWebView.postMessage accepts only one parameter with the data to be send and it has to be a string.
window.ReactNativeWebView.postMessage only accepts one argument which must be a string.
Here is a screen capture with the working app:

React Native Firebase - Push data to Array allowing to display in a FlatList

I am new to React Native and struggling a little to get this working. I have realtime database in Firebase which contains 'mechanic' names. I would like to retrieve these names and display them in a list.
I would like to display this data in a list and then execute some function when the user clicks on either name. I thought adding the database data to an array then looping through the array to add it to my FlatList.
The problem now is that when I execute the code, the this.setState({ mechanicsList: mechanicsTemp }); returns an error.
Error
[Unhandled promise rejection: TypeError: this.setState is not a function.
(In 'this.setState({]
* src\screens\FindMechanics.js:28:30 in <unknown>
- node_modules\promise\setimmediate\core.js:37:14 in tryCallOne
- node_modules\promise\setimmediate\core.js:123:25 in <unknown>
- ... 8 more stack frames from framework internals
Full Code
import React, { Component } from 'react';
import { View, Text, SafeAreaView, TouchableOpacity, ScrollView, StyleSheet } from "react-native";
import { Card } from 'react-native-elements'
import firebase from "firebase/app";
export default class FindMechanics extends Component {
constructor(props) {
super(props);
this.state = {
mechanicsList: [],
isDataLoaded: false
}
}
componentDidMount() {
var query = firebase.database().ref("MechanicList").orderByKey();
query.once("value")
.then(function (snapshot) {
let mechanicsTemp = [];
snapshot.forEach(function (childSnapshot) {
// key will be the auth ID for each user
var key = childSnapshot.key;
var mechanicName = snapshot.child(key + '/name').val();
mechanicsTemp.push({ _name: mechanicName, _key: key });
});
mechanicsList = mechanicsTemp;
() => this.setState({ mechanicsList: mechanicsTemp }); // This does not execute it seems - main problem I believe
//this.setState({ mechanicsList: mechanicsTemp }); - This return a warning 'this.setState is not a function'
console.log(mechanicsList); //Prints data as expected
mechanicsTemp.forEach((mechanic) => {
console.log( mechanic._name); //Prints data as expected
});
});
}
render() {
//The Card element is empty - nothing shows.
console.log(this.state.mechanicsList) //This return Array [] which indicates it is empty
return (
<SafeAreaView style={styles.container}>
<ScrollView horizontal={true}>
<TouchableOpacity>
<Card style={styles.card}>
{
this.state.mechanicsList.map((u, i) => {
return (
<View key={i}>
<Text>{u._key}</Text>
<Text>{u._name}</Text>
</View>
);
})
}
</Card>
</TouchableOpacity>
</ScrollView>
</SafeAreaView>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#FFF'
},
paragraph: {
margin: 24,
fontSize: 18,
textAlign: 'center',
},
card: {
flex: 1,
width: '80%',
},
});
Console
Finished building JavaScript bundle in 384ms.
Running application on Android SDK built for x86.
Array []
1st thing, you have mechanics object in state so you need to access it like
console.log(this.state.mechanics)
2nd thing is that you are not updating state variable when you are having data, it should be like following
let mechanicsTemp = [];
snapshot.forEach(function (childSnapshot) {
// key will be the auth ID for each user
var key = childSnapshot.key;
var mechanicName = snapshot.child(key + '/name').val();
mechanicsTemp.push({_name: mechanicName, _key: key});
});
this.setState({ mechanics:mechanicsTemp })
I dunno if you still need help with this or not but I just used your code and I solved this.setState problem with binding. You can either use arrow function or bind your function:
.then(function (snapshot) {
// ..
}.bind(this));

Cancel a fetch request in react-native

Is there any way to abort a fetch request on react-native app ?
class MyComponent extends React.Component {
state = { data: null };
componentDidMount = () =>
fetch('http://www.example.com')
.then(data => this.setState({ data }))
.catch(error => {
throw error;
});
cancelRequest = () => {
//???
};
render = () => <div>{this.state.data ? this.state.data : 'loading'}</div>;
}
i tried the abort function from AbortController class but it's not working !!
...
abortController = new window.AbortController();
cancelRequest = () => this.abortController.abort();
componentDidMount = () =>
fetch('http://www.example.com', { signal: this.abortController.signal })
....
Any help please !
You don't need any polyfill anymore for abort a request in React Native 0.60 changelog
Here is a quick example from the doc of react-native:
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* #format
* #flow
*/
'use strict';
const React = require('react');
const {Alert, Button, View} = require('react-native');
class XHRExampleAbortController extends React.Component<{}, {}> {
_timeout: any;
_submit(abortDelay) {
clearTimeout(this._timeout);
// eslint-disable-next-line no-undef
const abortController = new AbortController();
fetch('https://facebook.github.io/react-native/', {
signal: abortController.signal,
})
.then(res => res.text())
.then(res => Alert.alert(res))
.catch(err => Alert.alert(err.message));
this._timeout = setTimeout(() => {
abortController.abort();
}, abortDelay);
}
componentWillUnmount() {
clearTimeout(this._timeout);
}
render() {
return (
<View>
<Button
title="Abort before response"
onPress={() => {
this._submit(0);
}}
/>
<Button
title="Abort after response"
onPress={() => {
this._submit(5000);
}}
/>
</View>
);
}
}
module.exports = XHRExampleAbortController;
I've written quite a bit actually about this subject.
You can also find the first issue about the OLD lack of AbortController in React Native opened by me here
The support landed in RN 0.60.0 and you can find on my blog an article about this and another one that will give you a simple code to get you started on making abortable requests (and more) in React Native too. It also implements a little polyfill for non supporting envs (RN < 0.60 for example).
You can Actually achieve this by installing this polyfill abortcontroller-polyfill
Here is a quick example of cancelling requests:
import React from 'react';
import { Button, View, Text } from 'react-native';
import 'abortcontroller-polyfill';
export default class HomeScreen extends React.Component {
state = { todos: [] };
controller = new AbortController();
doStuff = () => {
fetch('https://jsonplaceholder.typicode.com/todos',{
signal: this.controller.signal
})
.then(res => res.json())
.then(todos => {
alert('done');
this.setState({ todos })
})
.catch(e => alert(e.message));
alert('calling cancel');
this.controller.abort()
}
render(){
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Details Screen</Text>
<Button title="Do stuff" onPress={() => { this.doStuff(); }} />
</View>
)
}
}
So basically in this example, once you click the 'doStuff' button, the request is immediately cancelled and you never get the 'done' alert. To be sure, it works, try and comment out these lines and click the button again:
alert('calling cancel');
this.controller.abort()
This time you will get the 'done' alert.
This is a simple example of hoe you can cancel a request using fetch in react native, feel free to adopt this to your own use case.
Here is a link to a demo on snackexpo https://snack.expo.io/#mazinoukah/fetch-cancel-request
hope it helps :)
the best solution is using rxjs observables + axios/fetch instead of promises, abort a request => unsubscribe an observable :
import Axios from "axios";
import {
Observable
} from "rxjs";
export default class HomeScreen extends React.Component {
subs = null;
doStuff = () => {
let observable$ = Observable.create(observer => {
Axios.get('https://jsonplaceholder.typicode.com/todos', {}, {})
.then(response => {
observer.next(response.data);
observer.complete();
})
});
this.subs = observable$.subscribe({
next: data => console.log('[data] => ', data),
complete: data => console.log('[complete]'),
});
}
cancel = () =>
if (this.subs) this.subs.unsubscribe()
componentWillUnmount() {
if (this.subs) this.subs.unsubscribe();
}
}
That is it :)

Load thumbnail image on failure response from uri - React Native

I need to render image tag in a loop based on array length,
My image tag looks similar to this
<Image
style={{width: 50, height: 50}}
source={{uri: 'https://facebook.github.io/react/img/logo_og1.png'}}
//if image fails to load from the uri then load from'./img/favicon.png'
onError={(error) => {console.log('loadinf',error)}}
/>
If error occurs while fetching from Uri i.e 404 error retry for sometime or show default local image.
How to do that in react native?
You put in a loading image in the place of real image using defaultSource property but it is only available to iOS as of this moment. We can achieve the other thing you wrote that it should display a local image in case it couldn't load the image from the internet by the following approach.
Store the original image URI in state.
Use this URI from state in source of the image.
When onError function is called change this state variable to your local image.
Example:
import React, {Component} from 'react';
import {
View,
Image
} from 'react-native';
export default class Demo extends Component {
constructor(props) {
super(props);
this.state = {
image: {
uri: 'something demo'
}
}
}
render() {
return <View>
<Image
source={ this.state.image }
onError={(a) => {
this.setState({
image: require('image.png')
});
}}
style={{ height: 100, width: 100 }}
/>
</View>
}
}
A dirty hack for retrying the image would be something like this:
let counter = 0;
export default class reactNativePange extends Component {
constructor(props) {
super(props);
this.state = {
image: {
uri: 'something demo'
},
failed: false
}
}
render() {
return (
<View style={styles.container}>
<Image
source={ this.state.failed ? require('./image.png') : { uri: 'something demo' } }
onError={(a) => {
if(counter >= 3) {
this.setState({
failed: true
});
} else {
counter++;
this.setState({
failed: true
});
setTimeout(() => {
this.setState({
failed: false
});
}, 1)
}
}}
style={{ height: 100, width: 100 }}
/>
</View>
);
}
}
As I mentioned that this is a dirty hack as the image might flicker during the retry.

Categories

Resources