i am using a PhotoGrid Library in react native to populate the list of photo on my apps. how to call a function from the render function ? it show this error when i call a function called "deva" on my OnPress method in <Button onPress={()=>{this.deva()}}><Text>Bondan</Text></Button> . here is my code...
import React from 'react';
import { StyleSheet, Text, View, WebView, TouchableOpacity, Image, Alert, Dimensions} from 'react-native';
import {DrawerNavigator} from 'react-navigation'
import {Container, Header, Button, Icon, Title, Left, Body, Right, Content} from 'native-base'
import PhotoGrid from 'react-native-photo-grid'
import HomeScreen from './HomeScreen'
export default class Recomended extends React.Component {
constructor() {
super();
this.state = { items: [],
nama : ""
}
}
goToBufetMenu(){
this.props.navigation.navigate("BufetMenu");
}
componentDidMount() {
// Build an array of 60 photos
let items = Array.apply(null, Array(60)).map((v, i) => {
return { id: i, src: 'http://placehold.it/200x200?text='+(i+1) }
});
this.setState({ items });
//this.setState({ nama: "Bondan"});
//this.props.navigation.navigate("BufetMenu");
}
deva() {
Alert.alert('deva');
}
render() {
return (
<Container style={styles.listContainer}>
<PhotoGrid
data = { this.state.items }
itemsPerRow = { 3 }
itemMargin = { 3 }
renderHeader = { this.renderHeader }
renderItem = { this.renderItem }
style={{flex:2}}
/>
</Container>
);
}
renderHeader() {
return(
<Button onPress={()=>{this.deva()}}><Text>Bondan</Text></Button>
);
}
renderItem(item, itemSize) {
return(
<TouchableOpacity
key = { item.id }
style = {{ width: itemSize, height: itemSize }}
onPress = { () => {
this.deva();
}}>
<Image
resizeMode = "cover"
style = {{ flex: 1 }}
source = {{ uri: item.src }}
/>
<Text>{item.src}</Text>
</TouchableOpacity>
)
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
height: 587,
},
gridText: {
color: '#fff',
textAlign: 'center',
fontStyle: 'normal',
fontSize : 12
},
listContainer: {
height: Dimensions.get('window').height - (Dimensions.get('window').height*53/100),
}
});
You are loosing context of this. You need to either use arrow functions or bind the functions.
Example
constructor() {
super();
this.state = { items: [],
nama : ""
};
this.renderHeader = this.renderHeader.bind(this);
this.renderItem = this.renderItem.bind(this);
}
OR
renderHeader = () => {
// rest of your code
}
renderItem = (item, itemSize) => {
// rest of your code
}
Either change your deva method definition to an arrow function -
deva= () => {
Alert.alert('deva');
}
Or bind the deva method to this inside your constructor
constructor() {
super();
this.state = { items: [],
nama : ""
}
this.deva = this.deva.bind(this)
}
You get the error because when the deva method is invoked using this.deva(), the javascript runtime cannot find the property/function deva on the this it's called with (which is the anonymous callback passed to onPress in this case). But if you bind this to deva beforehand, the correct this is being searched by the javascript runtime.
Related
I have the following code:
import React from 'react';
import {StyleSheet, View} from 'react-native';
interface MyButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
dataCode: string;
}
class MyButton extends React.Component<
MyButtonProps & React.HTMLProps<HTMLButtonElement>,
{}
> {
render() {
return <button {...this.props} />;
}
}
const App = () => {
const _onPressButton = (event: any) => {
let params: string = (event.currentTarget as MyButton).props.dataCode;
fetch(`http://10.18.1.19/switch?${params}`);
};
return (
<View style={styles.container}>
<View style={styles.buttonContainer}>
<MyButton
dataCode="binary=111111111111111111111111&protocol=1&pulselength=3"
onClick={_onPressButton}
title="Test"
/>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
},
buttonContainer: {
margin: 20,
},
});
export default App;
and I have error message when I use button ws. Button.
ReactNativeJS: Invariant Violation: View config getter callback for
component button must be a function (received undefined). Make
sure to start component names with a capital letter.
How can I extend a HTML button element?
I'm trying to get the value from days_free from an array within an object returned from a rest api. However, I get the error: Uncaught TypeError: this.state.user.map is not a function
The data is structured like so:
{
"available": true,
"offers": [
{
"days_free": 30,
"description": "Woo!"
}
]
}
I'm trying to map the array within the object and get the value with
const daysFree = this.state.user.map((daysFree, i) => {
return (
<span>{daysFree.days_free}</span>
)
})
From my component:
class CancelOffer extends React.Component {
constructor (props) {
super(props)
this.state = {
user: []
}
this.processData = this.processData.bind(this)
}
componentDidMount () {
this.fetchContent(this.processData)
}
fetchContent (cb) {
superagent
.get('/api/data')
.then(cb)
}
processData (data) {
this.setState({
user: data.body
})
}
render () {
const content = this.props.config.contentStrings
const daysFree = this.state.user.map((daysFree, i) => {
return (
<span>{daysFree.days_free}</span>
)
})
return (
<div className='offer'>
<h2 className='offer-heading md'>Heading</h2>
<p className='offer-subpara'>text {daysFree} </p>
<div className='footer-links'>
<a href='/member' className='btn btn--primary btn--lg'>accept</a>
<a href='/' className='cancel-link'>cancel</a>
</div>
</div>
)
}
}
export default CancelOffer
data.body is an object, if you want to loop over the offers, you need to do
processData (data) {
this.setState({
user: data.body.offers
})
}
I would like to display InfoWindow on Marker click. I followed some tutorials and I used react-google-maps for my project. I would like my app to work like this: "https://tomchentw.github.io/react-google-maps/basics/pop-up-window" but my code is a little bit different.
class Map extends React.Component {
handleMarkerClick(){
console.log("Clicked");
}
handleMarkerClose(){
console.log("CLOSE");
}
render(){
const mapContainer= <div style={{height:'100%',width:'100%'}}></div>
//fetch markers
const markers = this.props.markers.map((marker,i) => {
return (
<Marker key={i} position={marker.location} showTime={false} time={marker.time} onClick={this.handleMarkerClick} >
{
<InfoWindow onCloseClick={this.handleMarkerClose}>
<div>{marker.time}</div>
</InfoWindow>
}
</Marker>
)
})
/* set center equals to last marker's position */
var centerPos;
if(markers[markers.length-1]!== undefined)
{
centerPos=markers[markers.length-1].props.position;
}
else {
centerPos={};
}
return (
<GoogleMapLoader
containerElement={mapContainer}
googleMapElement={
<GoogleMap
defaultZoom={17}
center={centerPos}
>
{markers}
</GoogleMap>
}/>
);
}
}
export default Map;
I got "this.props.markers" from another class component, which fetching data from URL. I am almost sure, that it is easy problem to solve. Currently on marker click in console I got "Clicked" and on Marker close "CLOSE" as you can guess from above code it is because of handleMarkerClick() and handleMarkerClose(). I want to have pop-window with InfoWindow.
What should I do to make it work?
Here is heroku link : App on heroku
Hi I came across the same requirement. I did this (I am using redux and redux-thunk) :
GoogleMap.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import {
withGoogleMap,
GoogleMap,
Marker,
InfoWindow
} from 'react-google-maps';
import { onMarkerClose } from '../actions/Cabs';
const GettingStartedGoogleMap = withGoogleMap(props => (
<GoogleMap
defaultZoom={12}
defaultCenter={{ lat: 12.9716, lng: 77.5946 }}
>
{props.markers.map( (marker, index) => (
<Marker {...marker} onClick={() => props.onMarkerClose(marker.key)}>
{marker.showInfo &&(
<InfoWindow onCloseClick={() => props.onMarkerClose(marker.key)}>
<div>
<h1>Popover Window</h1>
</div>
</InfoWindow>
)}
</Marker>
))}
</GoogleMap>
));
class CabType extends Component{
constructor(props){
super(props);
}
render(){
if(this.props.cabs.length === 0){
return <div>loading...</div>
}
return(
<div className="map-wrapper">
<GettingStartedGoogleMap
containerElement={
<div style={{ height: '100%' }} />
}
mapElement={
<div style={{ height: '100%' }} />
}
onMarkerClose = {this.props.onMarkerClose}
markers={this.props.showMap ? this.props.markers : []}
/>
</div>
)
}
}
export default connect(store => {return {
cabs : store.cabs,
markers: store.markers
}}, {
onMarkerClose
})(CabType);
Action.js
const getMarkers = (cabs , name) => dispatch => {
let markers = [];
let data = {};
cabs.map(cab => {
if(cab.showMap){
data = {
position: {
lat : cab.currentPosition.latitude,
lng : cab.currentPosition.longitude
},
showInfo: false,
key: cab.cabName,
icon: "/images/car-top.png",
driver: cab.driver,
contact: cab.driverContact,
};
markers.push(data);
}
});
dispatch(emitMarker(markers));
};
function emitSetMarker(payload){
return{
type: SET_MARKER,
payload
}
}
export const onMarkerClose = (key) => dispatch => {
dispatch(emitSetMarker(key))
};
RootReducer.js
import { combineReducers } from 'redux';
import { cabs } from "./Cabs";
import { markers } from "./Markers";
const rootReducer = combineReducers({
cabs,
markers,
});
export default rootReducer;
MarkerReducer.js
import { GET_MARKERS, SET_MARKER } from "../types"
export const markers = (state = [], action) => {
switch (action.type){
case GET_MARKERS:
return action.payload;
case SET_MARKER:
let newMarker = state.map(m => {
if(m.key === action.payload){
m.showInfo = !m.showInfo;
}
return m;
});
return newMarker;
default: return state;
}
};
Sorry for a long post but this is code which is tested and running. Cheers!
I'm trying to render markers on a Google map from a React-Redux state but I'm getting the errors "Cannot read property 'map' of undefined" and "Cannot read property '_currentElement' of null"
Here's my component:
class Map extends Component {
constructor() {
super()
this.state = {}
}
componentDidMount() {
APIManager.get('/api/hike', null, (err, response) => {
if (err) {
return
}
this.props.hikesReceived(response.results)
})
}
componentDidUpdate() {
console.log('updated list of hikes ' + JSON.stringify(this.props.hikes))
}
render() {
const hikes = this.props.hikes.map((hike, i) => {
const hikeMarker = {
latlng: {
lat: hike.location.lat,
lng: hike.location.lng
}
}
return <Markey key={i} {...hikeMarker} />
})
return (
<GoogleMapLoader
containerElement = { this.props.mapContainer }
googleMapElement = {
<GoogleMap
defaultZoom={10}
defaultCenter={this.props.center}
options={{streetViewControl: false, mapTypeControl: false}}
onClick={this.addMarker.bind(this)} >
<Marker {...hikeMarker}/>
</GoogleMap>
} />
)
}
}
const stateToProps = (state) => {
return {
hikes: state.hike.list,
}
}
const dispatchToProps = (dispatch) => {
return {
hikesReceived: (hikes) => dispatch(actions.hikesReceived(hikes)),
}
}
export default connect(stateToProps, dispatchToProps)(Map)
I know it's an async problem because the console.log() happens twice. The first time it's empty and the second time it renders with the data. The markers also display with no error when I'm using dummy data.
How do I go about telling the map to wait to render, or re-render, once there is data in this.props?
Half of the problem was names changing in the database. The other half was solved with:
if (this.props.hikes == null || undefined) {return: false}
I hooked up the react-native-tab-navigator to a redux reducer using react-native-navigation-redux-helpers. When I press on a tab, the reducer changes the current tab state inside of the redux store. However, there is a lag between when I tap on a tab and when the tab is "selected" and renders the component. Is there any way to speed up the process of selecting a tab and rendering the view?
Here is my ApplicationTabs component:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { actions as navigationActions } from 'react-native-navigation-redux-helpers';
import { Tabs, Tab, Icon } from 'react-native-elements';
import Feed from '../feed';
import Inbox from '../inbox';
import { openDrawer } from '../../actions/drawer';
import styles from './styles.js';
const { jumpTo } = navigationActions;
class ApplicationTabs extends Component {
constructor(props) {
super(props)
}
_openDrawer() {
this.props.openDrawer();
}
_renderTabContent(tab) {
switch (tab.key) {
case 'feed':
return <Feed />;
case 'request':
return <Inbox />
default:
return <Feed />;
}
}
_changeTab (tab) {
const { tabs } = this.props;
this.props.jumpTo(tab.key, tabs.key)
}
render() {
const { tabs, drawerState } = this.props;
const children = tabs.routes.map((tab, i) => {
return (
<Tab
selected={tabs.index === i}
title={tab.title}
renderIcon={() => <Icon containerStyle={styles.iconContainer} iconStyle={styles.iconStyle} type='Entypo' name={tab.iconName} size={33} />}
onPress={() => this._changeTab(tab)}
titleStyle={styles.titleStyle}>
{this._renderTabContent(tab)}
</Tab>
)
});
return (
<Tabs tabBarStyle={styles.tabBarStyle}>
<Tab
selected={drawerState === 'opened'}
title='Menu'
renderIcon={() => <Icon containerStyle={styles.iconContainer} iconStyle={styles.iconStyle} type='Entypo' name='menu' size={33} />}
onPress={() => this._openDrawer()}
titleStyle={styles.titleStyle}>{this._renderTabContent(tabs.key)}</Tab>
{children}
</Tabs>
);
}
}
function mapDispatchToProps(dispatch) {
return {
jumpTo: (keyOrIndex, key) => dispatch(jumpTo(keyOrIndex, key)),
openDrawer: () => dispatch(openDrawer()),
};
}
function mapStateToProps(state) {
return {
tabs: state.tabs,
drawerState: state.drawer.drawerState
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ApplicationTabs);
Here is the tabReducer:
import { tabReducer } from 'react-native-navigation-redux-helpers';
const tabs = {
routes: [
{ key: 'feed', title: 'Feed', iconName:'home'},
{ key: 'request', title: 'Request', iconName: 'camera-alt' },
{ key: 'memoryBox', title: 'Memory Box', iconName: 'photo' },
{ key: 'search', title: 'Search', iconName: 'search' }
],
key: 'ApplicationTabs',
index: 0
};
export default tabReducer(tabs);