react-native-tab-navigator and redux performance issue - tabs

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);

Related

How i can do my component in React.js have a individual behavior?

I'm implementing a Like and Dislike Button, and I wanna that when I click them will be with other colors, but just the clicked component, when I click all buttons change the state, can anybody help me?
`
const indexPost = async () => {
const data = await api.get('/api/posts')
if(data.data.length !=0){
const dataArray = data.data
if(dataArray.length === 0) {
return
}else{
return(
setPost(dataArray.map( data => (
<Post key={data._id} id={data._id} title={data.title} text={data.text}>
<Like id={data._id}></Like>
</Post>
)))
)
}
}
}
export default function Like({itemId}) {
const context = useContext(notificationContext)
const {isLoved, Like, Loved, Unlike, isLike, isUnlike, setIsLike, setIsUnlike, setIsLoved } = context
return(
<div className={styles.likeContainer} key={itemId}>
{isLike ? (
<button className={styles.likeContent} onClick={() => setIsLike(false)}><Icon.ThumbsUp className={styles.Icon} fill="#5CB0BB" ></Icon.ThumbsUp></button>) :
(<button className={styles.likeContent} onClick={() => Like() }><Icon.ThumbsUp className={styles.Icon} ></Icon.ThumbsUp></button>)}
{isLoved ?
(<button className={styles.likeContent} onClick={() => setIsLoved(false)}><Icon.Heart className={styles.Icon} fill="red" ></Icon.Heart> </button>) :
(<button className={styles.likeContent} onClick={() => Loved() }><Icon.Heart className={styles.Icon} ></Icon.Heart></button>)}
{isUnlike ? (
<button className={styles.likeContent} onClick={() => setIsUnlike(false)}><Icon.ThumbsDown className={styles.Icon} fill="#702BA6" ></Icon.ThumbsDown> </button>) :
(<button className={styles.likeContent} onClick={() => Unlike()}><Icon.ThumbsDown className={styles.Icon} ></Icon.ThumbsDown></button>
)}
</div>
)
};
I have implemented the similar one in my project, it is very basic , it shows how to update the likes , you need to handle the cases of user authentication and stuff
App.js
import { useState, useEffect, createContext, useReducer } from "react";
import { updateArrayOfObj } from "./utils";
import AllPosts from "./AllPosts";
export const PostsContext = createContext();
const initialState = {
posts: [
{
_id: "1",
name: "Browny",
image: "http://placekitten.com/200/310",
likes: 0,
love: 0,
dislikes: 0
},
{
_id: "2",
name: "Blacky",
image: "http://placekitten.com/200/320",
likes: 0,
love: 0,
dislikes: 0
},
{
_id: "3",
name: "SnowWhite",
image: "http://placekitten.com/200/300",
likes: 0,
love: 0,
dislikes: 0
}
]
};
const reducer = (state, action) => {
switch (action.type) {
case "UPDATE_POST":
return {
...state,
posts: updateArrayOfObj(
state.posts,
action.payload.obj,
"_id",
action.payload._id
)
};
case "CREATE_POST":
return {
...state,
posts: [...state.posts, ...action.payload.data]
};
case "DELETE_POST":
return {
...state,
posts: state.posts.filter((ele) => ele._id !== action.payload._id)
};
default:
return state;
}
};
export default function App() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<PostsContext.Provider
value={{
state,
dispatch
}}
>
<div className="App">
<AllPosts />
</div>
</PostsContext.Provider>
);
}
PostsAll.js
import Post from "./Post";
import { PostsContext } from "./App";
import { useContext } from "react";
export default function AllPosts() {
const { state } = useContext(PostsContext);
return (
<div className="allPosts">
{state.posts.map((item) => {
return (
<Post
name={item.name}
image={item.image}
likes={item.likes}
love={item.love}
dislikes={item.dislikes}
id={item._id}
key={item._id}
/>
);
})}
</div>
);
}
Post.js
import { PostsContext } from "./App";
import { useContext } from "react";
export default function Post(props) {
const { state, dispatch } = useContext(PostsContext);
const handleUserInteraction = (type, id) => {
dispatch({
type: "UPDATE_POST",
payload: {
obj: { [type]: props[type] + 1 },
_id: id
}
});
};
return (
<div className="post">
<h3>{props.name}</h3>
<img src={props.image} alt="cat" />
<br />
<button onClick={() => handleUserInteraction("likes", props.id)}>
{props.likes} Like
</button>{" "}
<button onClick={() => handleUserInteraction("love", props.id)}>
{props.love} Love
</button>{" "}
<button onClick={() => handleUserInteraction("dislikes", props.id)}>
{props.dislikes} Dislike
</button>
</div>
);
}
You can refer to this codesandbox to implement the same
You can use onClick() on each like button and attach it with a function, then you can get the value of that particular like with e.currentTarget.id and change its css/style the way you want.
const handleClick=(e)=>
{
console.log(e.currentTarget.id);
}

react native 2 dropdown depend on first one

I am trying to connect two dropdown with in react native,
same as country and city
if i select any country it should load cities from that country to second drop-down
all data is in a external json file
but nothing is loading in both drop down (picker)
json file :
{
"interest": [
{
"RAW_MATERIAL":["abc","cde"]
},
{
"OEM_PARTS":["xyz","qwer"]
},
{
"CONSUMABLES":["poiu","fjgl"]
},
{
"SERVICE":["xvcbv","qweiw"]
}
],
}
react native picker i use:
import React, { Component } from 'react';
import { Container,Picker,Button } from 'native-base';
const cData = require('../data.json');
export default class Vendorsupplies extends Component {
constructor(props) {
super(props);
this.state = {
interest:'',
interest2:''
};
}
interest(value: string) {
this.setState({
interest: value
});
}
interest2(value: string) {
this.setState({
interest2: value
});
}
<Picker
note
mode="dropdown"
style={{ width: 120 }}
selectedValue={this.state.interest}
onValueChange={this.interest.bind(this)}
name="intre"
>
{cData.interest.map((number) =>
<Picker.Item label={number.interest_in} value={number.interest_in} />
)}
</Picker>
<Picker
note
mode="dropdown"
style={{ width: 120 }}
selectedValue={this.state.intre2.interest}
onValueChange={this.intre2.interest.bind(this)}
name="intre2"
>
{cData.interest.map((number) =>
<Picker.Item label={number.intre2.interest_in} value={number.intre2.interest_in} />
)}
</Picker>
You can try it like that, I just done it with select but you will get the idea.
const cData = {
interest: [
{
RAW_MATERIAL: ['abc', 'cde'],
},
{
OEM_PARTS: ['xyz', 'qwer'],
},
{
CONSUMABLES: ['poiu', 'fjgl'],
},
{
SERVICE: ['xvcbv', 'qweiw'],
},
],
};
class TodoApp extends React.Component {
constructor(props) {
super(props);
this.state = {
interest: 'RAW_MATERIAL',
interest2: '',
};
}
renderOption() {
const el = cData.interest.find(
interest => Object.keys(interest)[0] === this.state.interest
);
if (el) {
return el[this.state.interest].map(option => (
<option value={option}>{option}</option>
));
}
return <option>empty</option>;
}
render() {
return (
<React.Fragment>
<select
value={this.state.interest}
onChange={e => {
e.persist();
this.setState(prev => ({
...prev,
interest: e.target.value,
}));
}}
>
{cData.interest.map(el => (
<option value={Object.keys(el)}>{Object.keys(el)}</option>
))}
</select>
<select
value={this.state.interest2}
onChange={e => {
e.persist();
this.setState(prev => ({ ...prev, interest2: e.target.value }));
}}
>
{this.renderOption()}
</select>
</React.Fragment>
);
}
}
ReactDOM.render(<TodoApp />, document.querySelector("#app"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>

Array JSON with ListView in React-Native

I have an issue, I'm trying to make a little database offline inside a JSON, like this:
[
{
title: "Carros",
carros: [
{
nome: "Ferrari"
}
]
},
{
title: "Motos",
carros: [
{
nome: "Suzuki"
}
]
}
];
From now, my HomeScreen lists the categories as "Carros" and "Motos", but when I want to enter in subtopic like "carros", but I can't.
Currently using a ListView
{ list.map((item, i) => (
<View>
<TouchableOpacity
onPress={() =>
this.props.navigation.navigate("Listagem", {
itemName: item.title
})
}
>
<ListItem key={item.title} title={item.title} />
</TouchableOpacity>
</View>
))
}
How to get child items?
Carros -> carros
Motos -> carros ???
Motos -> motos
If you only have "carros", there are no "motos". Hope you get my point.
In order to get the carros Object inside the Listagem screen you need to pass it as an prop.
<TouchableOpacity
onPress={() =>
this.props.navigation.navigate("Listagem", {
itemName: item.title,
childItems: item.carros
})
}
>
In you Listagem screen you will get these properties.
constructor() {
super();
const { navigation } = this.props;
const itemName = navigation.getParam('itemName', 'defaultName');
const childItems = navigation.getParam('childItems', []);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows(childItems),
};
}
render(){
return(
<ListView
dataSource={this.state.dataSource}
renderRow={(rowData) => <Text>{rowData.nome}</Text>}
/>
)
}
You can do this in your home screen:
import React, { Component } from 'react';
import {FlatList, ScrollView} from 'react-native';
import { List, ListItem } from 'react-native-elements'; //this is not necessary. You may use it for aesthetic purposes only.
const cars = [
{
title: "Carros",
carros: [
{
nome: "Ferrari"
}
]
},
{
title: "Motos",
carros: [
{
nome: "Suzuki"
}
]
}
];
export default class Home extends Component {
renderCars() {
return (
<List
containerStyle={{ borderTopWidth: 0, borderBottomWidth: 0 }}
>
<FlatList
data={cars}
keyExtractor={item => item.title}
renderItem={({ item }) => (
<ListItem
titleStyle={yourstyles.title}
hideChevron
title={item.title}
onPress={() => this.props.navigation.navigate(
'ChildPage',
{
cars: item //pass the entire item as a prop. This way you will have all its subtopics in your child page
}
)}
/>
)}
/>
</List>
)
}
render() {
return (
<ScrollView>
{this.renderCars()}
</ScrollView>
)
}
}
In your child page where you want to list the subtopics of 'carros' for example. Remember you passed the entire item as a prop, so that prop will be available for you in the child page.
So now you can do this in your child page:
//ChildPage
import React, { Component } from 'react';
import {FlatList, ScrollView, View, Text} from 'react-native';
import { List, ListItem } from 'react-native-elements'; //this is not necessary. You may use it for aesthetic purposes only.
export default class ChildPage extends Component {
renderSubtopics() {
const { cars } = this.props.navigation.state.params; // remember the entire item you passed down from Home, we will use it here
return (
<List
containerStyle={{ borderTopWidth: 0, borderBottomWidth: 0 }}
>
<FlatList
data={cars.carros}
keyExtractor={item => item.nome}
renderItem={({ item }) => (
<ListItem
titleStyle={yourstyles.title}
hideChevron
title={item.nome}
onPress={() => //do whatever )}
/>
)}
/>
</List>
)
}
render() {
return (
<ScrollView>
<View>
<Text>
{this.props.navigation.state.params.cars.title}
<Text>
</View>
{this.renderSubtopics()}
</ScrollView>
)
}
}

calling function from PhotoGrid render function Library

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.

React+Redux - show InfoWindow on Marker click

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!