I'm building an app with React-Native.
I'm trying to render a URL with a Webview, even some basic HTML or a site like m.facebook.com will not render in iOS. Tried different solution like other Webviews but none give results.
When in Android i don't have any problems and the page(s) will render just fine. Am i missing some key information.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { actions } from 'react-native-navigation-redux-helpers';
import { Container, Header, Title, Content, Button, Icon, Footer } from 'native-base';
import { Platform, WebView } from 'react-native';
import FooterTabs from '../../components/footerTabs/FooterTabs';
import { openDrawer } from '../../actions/drawer';
import { setWebsiteUrl } from '../../actions/website';
import styles from './styles';
const {
pushRoute,
popRoute,
} = actions;
class Website extends Component {
constructor(props, context) {
super(props, context);
this.state = {};
this.openFrame = this.openFrame.bind(this);
}
popRoute() {
this.props.popRoute(this.props.navigation.key);
}
pushRoute(route, index) {
this.props.pushRoute({ key: route, index: 1 }, this.props.navigation.key);
}
openFrame(url, name) {
this.props.setWebsiteUrl(url, name);
this.pushRoute('website', 2);
}
render() {
const { props } = this;
const { website, totalQuantity } = props;
const { frameUrl, frameName } = website;
return (
<Container style={styles.container} theme={deenTheme}>
<Header toolbarDefaultBg="#FFF" toolbarTextColor="FBFAFA">
<Button transparent onPress={this.props.openDrawer}>
<Icon name="ios-menu" />
</Button>
<Title style={styles.headerText}>{frameName}</Title>
<Button transparent onPress={() => this.openFrame('/cart', 'Winkelwagen')}>
<Icon style={{ fontSize: 25 }} name="md-basket" />
</Button>
<Button transparent>
<Icon style={{ fontSize: 25 }} name="md-search" />
</Button>
</Header>
<Content>
<WebView
source={{ uri: frameUrl }}
startInLoadingState
javaScriptEnabledAndroid
javaScriptEnabled
domStorageEnabled
scrollEnabled
style={{ flex: 1, width: 320 }}
/>
</Content>
<Footer theme={deenTheme}>
<FooterTabs />
</Footer>
</Container>
);
}
}
Website.propTypes = {
totalQuantity: React.PropTypes.number,
openDrawer: React.PropTypes.func,
setWebsiteUrl: React.PropTypes.func,
popRoute: React.PropTypes.func,
pushRoute: React.PropTypes.func,
navigation: React.PropTypes.shape({
key: React.PropTypes.string,
}),
};
function bindAction(dispatch) {
return {
openDrawer: () => dispatch(openDrawer()),
popRoute: key => dispatch(popRoute(key)),
pushRoute: (route, key) => dispatch(pushRoute(route, key)),
setWebsiteUrl: (url, name) => dispatch(setWebsiteUrl(url, name)),
};
}
const mapStateToProps = state => ({
navigation: state.cardNavigation,
website: state.website,
totalQuantity: state.cart.totalQuantity,
});
export default connect(mapStateToProps, bindAction)(Website);
UPDATE!
in iOS i need to configure style width & height else it won't work.
Related
I'm communicating to an API to update a state in my application. Everything works fine on PC. But on iOS the network request is 400 and the API does not return anything. Why? I'm running both through CORS anywhere and I've made sure to activate it both on my PC and my iOS device. One of the APIs that I'm communicating with in another file does work on both devices.
Home.js
import React, { useEffect, useState } from "react";
import { View, Text, SafeAreaView, ScrollView } from "react-native";
import { Divider } from "react-native-elements";
import BottomTabs from "../components/home/BottomTabs";
import Categories from "../components/home/Categories";
import HeaderTabs from "../components/home/HeaderTabs";
import RestaurantItems, {
localRestaurants,
} from "../components/home/RestaurantItems";
import SearchBar from "../components/home/SearchBar";
const YELP_API_KEY =
"x";
export default function Home({ navigation }) {
const [restaurantData, setRestaurantData] = useState(localRestaurants);
const [city, setCity] = useState("City");
const [activeTab, setActiveTab] = useState("Delivery");
const getRestaurantsFromYelp = () => {
const yelpUrl = `https://api.yelp.com/v3/businesses/search?term=restaurants&location=${city}`;
const corsUrl = `https://cors-anywhere.herokuapp.com/${yelpUrl}`;
const apiOptions = {
headers: {
Authorization: `Bearer ${YELP_API_KEY}`,
},
};
return fetch(corsUrl, apiOptions)
.then((res) => res.json())
.then((json) => {
console.log("JSON:", json.businesses);
setRestaurantData(json.businesses);
})
.catch((e) => console.log(e));
};
useEffect(() => {
getRestaurantsFromYelp();
}, [city, activeTab]);
useEffect(() => {
console.log("Restaurant Data Updated", restaurantData);
}, [restaurantData]);
return (
<SafeAreaView style={{ backgroundColor: "#eee", flex: 1 }}>
<View style={{ backgroundColor: "white", padding: 15 }}>
<HeaderTabs activeTab={activeTab} setActiveTab={setActiveTab} />
<SearchBar cityHandler={setCity} />
</View>
<ScrollView showsVerticalScrollIndicator={false}>
<Categories />
<RestaurantItems
restaurantData={restaurantData}
navigation={navigation}
/>
</ScrollView>
<Divider width={1} />
<BottomTabs />
</SafeAreaView>
);
}
I have a NextJS prototype live at https://www.schandillia.com/blog. The data displayed is being pulled off a Strapi installation at https://dev.schandillia.com/graphql. I also have the entire codebase up on Github at https://github.com/amitschandillia/proost/web (the frontend).
I'm using an Apollo client to interface with the graphql source. And also a service worker set up to enable PWA.
Everything's working fine except I'm unable to cache the query results at the browser. The service worker is able to cache everything else but the results of Apollo queries. Is there any way this could be enabled? The objective is:
To be able to use some kind of prefetching of query results at the server.
To be able to have the results cached at the browser via service worker.
The three files relevant to this issues are as follows:
Apollo Setup
// web/apollo/index.js
import { HttpLink } from 'apollo-link-http';
import { withData } from 'next-apollo';
import { InMemoryCache } from 'apollo-cache-inmemory';
// Set up cache.
const cache = new InMemoryCache();
// Configure Apollo.
const config = {
link: new HttpLink({
uri: 'https://dev.schandillia.com/graphql', // Server URL (must be absolute)
}),
cache,
};
export default withData(config);
Query Component
// web/pages/PostsList.jsx
import ReactMarkdown from 'react-markdown';
import gql from 'graphql-tag';
import { graphql } from 'react-apollo';
import { Fragment } from 'react';
import Typography from '#material-ui/core/Typography';
import CircularProgress from '#material-ui/core/CircularProgress';
const renderers = {
paragraph: props => <Typography variant="body1" gutterBottom {...props} />
};
const PostsList = ({ data: { error, posts } }) => {
let res = '';
if (error) res = (
<Typography variant="subtitle2" gutterBottom>
Error retrieving posts!
</Typography>
);
if (posts && posts.length) {
if (posts.length !== 0) {
// Payload returned
res = (
<Fragment>
{posts.map(post => (
<div>
<Typography variant="display1" gutterBottom>{post.title}</Typography>
<Typography variant="subtitle1" gutterBottom>{post.secondaryTitle}</Typography>
<Typography variant="subtitle2" gutterBottom>Post #{post._id}</Typography>
<ReactMarkdown source={post.body} renderers={renderers} />
</div>
))}
</Fragment>
);
} else {
res = (
// No payload returned
<Typography variant="subtitle2" gutterBottom>
No posts Found
</Typography>
);
}
} else {
res = (
// Retrieving payload
<CircularProgress />
);
}
return res;
};
const query = gql`
{
posts {
_id
title
secondaryTitle
body
}
}
`;
// The 'graphql' wrapper executes a GraphQL query and makes the results
// available on the 'data' prop of the wrapped component (PostsList)
export default graphql(query, {
props: ({ data }) => ({
data,
}),
})(PostsList);
Blog Page
// web/pages/blog.jsx
import React, { PureComponent, Fragment } from 'react';
import PropTypes from 'prop-types';
import Button from '#material-ui/core/Button';
import Typography from '#material-ui/core/Typography';
import { withStyles } from '#material-ui/core/styles';
import Head from 'next/head';
import Link from 'next/link';
import withRoot from '../lib/withRoot';
import PostsList from '../components/PostsList';
const styles = theme => ({
root: {
textAlign: 'center',
paddingTop: theme.spacing.unit * 20,
},
paragraph: {
fontFamily: 'Raleway',
},
});
class Blog extends PureComponent {
constructor(props) {
super(props);
}
componentDidMount() {
if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/serviceWorker.js'); }
}
render() {
const { classes } = this.props;
const title = 'Blog | Project Proost';
const description = 'This is the blog page';
return (
<Fragment>
<Head>
<title>{ title }</title>
<meta name="description" content={description} key="description" />
</Head>
<div className={classes.root}>
<Typography variant="display1" gutterBottom>
Material-UI
</Typography>
<Typography gutterBottom>
<Link href="/about">
<a>Go to the about page</a>
</Link>
</Typography>
<Typography gutterBottom>
<Link href="/blog">
<a>View posts</a>
</Link>
</Typography>
<Button variant="raised" color="primary">
Super Secret Password
</Button>
<Button variant="raised" color="secondary">
Super Secret Password
</Button>
</div>
<PostsList />
</Fragment>
);
}
}
Blog.propTypes = {
classes: PropTypes.shape({
root: PropTypes.string,
}).isRequired,
};
// Posts.propTypes = {
// classes: PropTypes.object.isRequired,
// };
export default withRoot(withStyles(styles)(Blog));
The service worker in question is as follows (redacted for brevity):
// web/offline/serviceWorker.js
const CACHE_NAME = '1b23369032b1541e45cb8e3d94206923';
const URLS_TO_CACHE = [
'/',
'/about',
'/blog',
'/index',
'apple-touch-icon.png',
'browserconfig.xml',
'favicon-16x16.png',
'favicon-194x194.png',
'favicon-32x32.png',
'favicon.ico',
'manifest.json',
];
// Call install event
self.addEventListener('install', (e) => {
e.waitUntil(
caches
.open(CACHE_NAME)
.then(cache => cache.addAll(URLS_TO_CACHE))
.then(() => self.skipWaiting())
);
});
// Call activate event
self.addEventListener('activate', (e) => {
// remove unwanted caches
e.waitUntil(
caches.keys().then((cacheNames) => {
Promise.all(
cacheNames.map((cache) => {
if (cache !== CACHE_NAME) {
return caches.delete(cache);
}
})
);
})
);
});
// Call fetch event
self.addEventListener('fetch', (e) => {
e.respondWith(
fetch(e.request).catch(() => caches.match(e.request))
);
});
Please advise!
I cannot open up the ResourcePicker for the life of me. I can see the state being changed from false to true when pressing the button, but the resourcepicker doesn't actually open.
<AppProvider apiKey={apiKey} shopOrigin={shopOrigin} >
<Page>
<TopBar pageTitle="Create Sale" primaryButton="Save" clickPrimaryButton={this.handleClick} />
<Layout sectioned={true}>
<Layout.AnnotatedSection title="Scheduled sale settings" description="Setup the discount and which products will be selected for the sale.">
<Card sectioned>
<FormLayout>
<TextField label="Sale name" onChange={() => { }} />
</FormLayout>
</Card>
<Card sectioned>
<FormLayout>
<DiscountValue />
</FormLayout>
</Card>
<Card>
<Card.Section>
<FormLayout>
<SaleCategory onSelect={this.handleSelect} />
</FormLayout>
</Card.Section>
{searchBar}
<Card.Section>
<Picker />
</Card.Section>
</Card>
</Layout.AnnotatedSection>
</Layout>
</Page>
</AppProvider>
and then the Picker Component
import React from 'react';
import { ResourcePicker, Button } from '#shopify/polaris';
class Picker extends React.Component {
constructor(props) {
super(props);
this.state = {
resourcePickerOpen: false,
}
}
render() {
return (
<div>
<pre>{JSON.stringify(this.state.resourcePickerOpen)}</pre>
<ResourcePicker
resourceType="Product"
open={this.state.resourcePickerOpen}
onSelection={({ selection }) => {
console.log('Selected products: ', selection);
this.setState({ resourcePickerOpen: false });
}}
onCancel={() => this.setState({ resourcePickerOpen: false })}
></ResourcePicker>
<Button onClick={() => this.setState({ resourcePickerOpen: !this.state.resourcePickerOpen })}>Open...</Button>
</div>
);
}
}
export default Picker;
I expect the picker to open, but it does not. What am I doing wrong?
Try to put your state declaration like the following code snippet
class Picker extends React.Component {
state = {
resourcePickerOpen: false,
}
...
}
you can delete your constructor
Is there a way I can fix this without using redux?
This is only happening on iOS, on android the AddListener works perfectly fine without it.
I have a component and I call the props.navigation.addListener on the componentDidMount functon.
Some code to help understand exactly where it breaks:
componentDidMount(){
var _this = this;
this.willBlurListener = this.props.navigation.addListener('willBlur', () => {
_this.timer.clearTimeout();
});
this.willFocusListener = this.props.navigation.addListener('willFocus', () => {
_this._action();
});
AppState.addEventListener('change', this._handleAppStateChange);
}
And then I use the component like this:
<Inactivity name='SomeNameView' navigation={ this.props.navigation }>
{this.renderDetails()}
</Inactivity>
Can you please try to use withNavigation function, it returns a HOC that has navigation in it props so you don't have to pass from the parent component to the child:
I created a simple app that uses this concept that probably can help you:
import React from 'react';
import {
View,
Text,
Button,
} from 'react-native';
import {
createStackNavigator,
withNavigation,
} from 'react-navigation';
class SomeComponent extends React.Component {
componentDidMount() {
this.willBlurListener = this.props.navigation.addListener('willBlur', () => {
this.someAction();
})
}
someAction() {
console.log('Some action is called!');
}
componentWillUnmount() {
this.willBlurListener.remove();
}
render() {
return (
<View>
<Text>Some Component</Text>
<Button
title={'Open settings'}
onPress={() => this.props.navigation.navigate('Settings')}
/>
</View>
)
}
}
const SomeComponentWithNavigation = withNavigation(SomeComponent);
class HomeScreen extends React.Component {
static navigationOptions = {
title: 'Home'
}
render() {
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<SomeComponentWithNavigation/>
<Text>Welcome to home screen!</Text>
</View>
)
}
}
class SettingsScreen extends React.Component {
static navigationOptions = {
title: 'Settings'
}
render() {
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text>Welcome to settings screen!</Text>
</View>
)
}
}
export default createStackNavigator(
{
Home: HomeScreen,
Settings: SettingsScreen,
},
);
I have used import { useNavigation } from '#react-navigation/native'; to achieve this. This could work for you as well.
Sample code example
import { useNavigation } from '#react-navigation/native';
class CurrentOrderClass extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
this.onFocusSubscribe = this.props.navigation.addListener('focus', () => {
// Your code
});
}
componentWillUnmount() {
this.onFocusSubscribe();
}
.
.
.
.
function CurrentOrder(props) {
const navigation = useNavigation(props)
return <CurrentOrderClass {...props} navigation={navigation} />
}
}
export default CurrentOrder;
You can also check to React Native docs https://reactnavigation.org/docs/navigation-events/
I found this a bit tricky and after looking into it for a bit, I come up with the following solution. Note that is tested on React Navigation 5.x.
import { useIsDrawerOpen } from "#react-navigation/drawer";
let lastDrawerStateIsOpened = false;
const DrawerComponent = (props) => {
const isOpened = useIsDrawerOpen();
if (lastDrawerStateIsOpened != isOpened) {
lastDrawerStateIsOpened = isOpened;
if (isOpened) {
// Do what needs to be done when drawer is opened.
}
}
};
Also, note that I'm using a functional component.
hello every one I have an application running locally with RoR everything works fine in the backend, also I can see the Json in Postman
http://127.0.0.1:3000/api/v1/articles
[
{
"id": 1,
"title": "Barack Obama",
"description": "Sabias que? el era muy pobre",
"avatar_content_type": "/system/articles/avatars/000/000/001/original/user-two.png?1509755893"
}
]
and also in my mobile emulator I can see the Title and description that's mean everything works fine. BUT I can't see the image :/ some advice?
ProductList.js
// step 1: import libraries
import React, { Component } from 'react';
import { ScrollView } from 'react-native';
import Product from './Product';
export default class ProductList extends Component {
state = {
products: []
}
componentDidMount() {
return fetch('http://MYIP:3000/api/v1/articles')
.then((response) => response.json())
.then((responseJson) => {
this.setState({products: responseJson})
})
}
render() {
var productList = this.state.products.map(function(item) {
return (
<Product title = { item.title }
description = { item.description }
image_url = { item.avatar_content_type }
key = { item.id }/>
)
})
return (
<ScrollView>
{ productList }
</ScrollView>
);
}
}
Product.js
// step 1: import libraries
import React, { Component } from 'react';
import { StyleSheet, Text, View, Alert, Image } from 'react-native';
// step 2: create component
export default class Product extends Component {
render() {
return (
<View>
<Image source={{uri: this.props.image_url }} style={{width: 60,height: 60,}}/>
<Text style = { style.textStyle }>{ this.props.title }</Text>
<Text style = { style.textStyle }>{ this.props.description }</Text>
</View>
);
}
}
const style = StyleSheet.create({
textStyle: {
fontSize: 20,
padding: 20
},
amountStyle: {
fontSize: 15,
paddingLeft: 20
}
})
I dont get exceptions or something similar, I'm not just be able to see the Image in my mobile emulator
It may be because It is empty during the first render as you are trying to perform an asynchronous operation
So what you could do is u can check by giving a condition I normally do it with lodash using _.isEmpty method
try this:
<Image source={{uri: (_.isEmpty(this.props.image_url) ? null : this.props.image_url) }}