undefined this.props.nnavigation.navigate('screen'), how to fix? - react-navigation

Im trying to use Drawer navigation. I have header.js components which has a hamburger icon to open the left navigation menu
//Header.js
import React from 'react';
import { Text, View, Image, TouchableHighlight} from 'react-native';
import { DrawerNavigator } from 'react-navigation';
// Make a component
const Header = (props, {navigate}) => {
const { textStyle, viewStyle, imgstyle } = styles;
return (
<View style={viewStyle}>
<TouchableHighlight onPress={()=> this.props.navigation.navigate('DrawerToggle')}> //here the error is coming
<Image style={{width:40, height:40}}
source={require('./../media/hamburger.png')}
/>
</TouchableHighlight>
<Text style={textStyle}>{props.headerText}</Text>
</View>
);
};
export default Header;
Here is App.js
const MyApp = DrawerNavigator({
Login: {
screen: Login,
},
Signup: {
screen: Signup,
},
});
export default class App extends React.Component {
render() {
return <MyApp />;
}
}
Here is one of the comoponent Login.js
import React, { Component } from 'react';
import {
Text, View, StyleSheet
} from 'react-native';
import Header from './Header';
export default class Login extends Component{
static navigationOptions = {
drawerLabel: 'Login',
};
render(){
return(
<View >
<Header headerText={'Login'}/>
<Text>Login Screen</Text>
</View>
);
}
}
I am not sure what I am missing to fix this. Please check my code.
EDITED:
I tried without "this" to but no luck. I am checking everywhere in the internet but couldn't find the exact solution or I couldn't get a solution to my mind. Can someone who is good with react native respond on this, Please?

The problem is - you use this keyword in a stateless component: this.props.navigation.navigate('DrawerToggle').
Try just props.navigation.navigate('DrawerToggle') since this is not pointing to the Header component instance in your case.
this in a stateless component is "inherited" from the calling scope (since stateless component is an "arrow" function) and likely points to window or global.

Related

How can I edit/Update the launch screen text in react-native app(Xcode)

I need to update my app name in launch/splash screen.
How can I update my launch screens name?
Can any one give the steps how to update the launch screen text?
I have Changed the LaunchScreen.xib in Xcode
<string key="text">Renamed </string>
You should :
1) Change class in index.js file from App.js to whichever class you want
import {AppRegistry} from 'react-native';
import routes from './src/Routes';
import {name as appName} from './app.json';
AppRegistry.registerComponent(appName, () => routes);
2) Create Route file
import React from 'react'
import { createAppContainer, createSwitchNavigator } from 'react-navigation';
import Splash from './Splash'
import Calc from './Calc'
const AppContainer = createAppContainer(
createSwitchNavigator(
{
Splash: Splash,
Calc: Calc,
},
{
initialRouteName: 'Splash',
},
),
)
export default class App extends React.Component {
render() {
return <AppContainer />;
}
}
3) Create desired splash screen
import React, { Component } from 'react';
import {
Text,
Image,
View
} from 'react-native';
import Style from './Style'
class Splash extends Component {
componentDidMount() {
setTimeout(() => {
this.props.navigation.navigate('Calc')
}, 1500)
}
render() {
return (
<View style={Style.splashContainer}>
<Image
source={{ uri: 'https://cdn2.iconfinder.com/data/icons/ios7-inspired-mac-icon-set/1024/Calculator_5122x.png' }}
style={Style.splashImage}
/>
<Text style={Style.splashText}>Calculator</Text>
</View>
)
}
}
export default Splash
In this last file you can design your splash screen however you want
That's how you can create custom launcher screen and splash screen in react native
Thank you

TouchableHighlight onpress Android

I am using react native to develop a simple Touchablehighlight element with onpress event. iOS is ok to go but Android takes few seconds to load a function each time. I have looked up few possible solutions such as TouchableWithoutFeedback, TouchableNativeFeedback, etc.
My idea is to click a TouchableHighlight -> call handlePress function -> alert('hello world') under a flatlist
Here is my code.
import React from "react";
import { Image, TouchableHighlight, Platform, TouchableNativeFeedback, View, Text } from "react-native";
import styles from "./Styles";
// Plugin
import FastImage from "react-native-fast-image";
class BacktoTop extends React.Component {
constructor(props) {
super(props);
}
_handlePress = () => {
alert('hello world')
}
render() {
return (
<TouchableHighlight
style={ styles.container }
underlayColor={"#ffffff"}
// onPress={this.props.handlePress }
onPress={this._handlePress }>
<FastImage
source={
(Platform.OS === 'ios') ?
require("../../images/back_to_top.png")
:
{
// 6 months at least 1 view no deletion
uri: 'https://image.ibb.co/grvFS8/back_to_top.png',
priority: FastImage.priority.cacheOnly,
}
}
resizeMode={FastImage.resizeMode.cover}
style={{ width: 35, height: 35 }} />
</TouchableHighlight>
);
}
}
export default BacktoTop;
Thank you so much for your help.
I think the more view components in the upper level to be rendered, the longer time it takes to load.
After I deleted some of my in parent and removed onMomentumScrollEnd the reaction is improved.
Thanks.

Images only load when directly required and are inconsistent when required through another module

What is consistent
[Edit: No longer consistent for whatever reason]
import React from 'react';
import {Image} from 'react-native';
import STYLE from './styles';
export default class OwnMarker extends React.Component {
render() {
return(
<Image
style={STYLE.marker}
source={require('../../../../resources/images/marker.png')}
resizeMethod="resize"
/>
);
}
}
This results in a consistently working solution.
What is inconsistent
import React from 'react';
import {Image} from 'react-native';
import STYLE from './styles';
//Note import
import Images from '../../../../resources/Images';
export default class OwnMarker extends React.Component {
render() {
return(
<Image
style={STYLE.marker}
source={Images.own_marker}
resizeMethod="resize"
/>
);
}
}
And the imported object looks like this:
//Images.js
module.exports = {
own_marker: require('./images/marker.png'),
}
This results in an inconsistent solution
Sometimes these images load and sometimes they don't. It might only render the image for 3 of the markers, none of them, all of them, 7 or them, or whatever other possibility between none and all of the markers loading.
How can I make this work from the import statement and why is it currently inconsistent?
It's hard to sleep at night with a solution like this, but this is the only one that has worked for me.
import React from 'react';
import {Image} from 'react-native';
import STYLE from './styles';
import Images from '../../../../resources/Images';
export default class OwnMarker extends React.Component {
constructor(props){
this.state = {
// HACK
initialRender: true
}
}
render() {
return(
<Image
style={STYLE.marker}
source={Images.own_marker}
resizeMethod="resize"
// HACK
onLayout={() => this.setState({ initialRender: false })}
key={`${this.state.initialRender}`}
/>
);
}
}

Re-rendering a single row of a list without re-rendering the entire list

we're trying to implement a contact list that works just like the new Material Design Google Contacts (you must enable the material design skin to see it) using material-ui.
Specifically we're trying to show a checkbox instead of the avatar on row hover.
We'd like to catch and re-render only the interested row (when hovered) and show the avatar/checkbox accordingly... this seems an easy task but we're not able to isolate the render to the hovered row (instead of re-rendering the entire list)
Do you have any suggestion on how to do something like this?
Our temporary solution uses a container component that handles the table:
When a row is hovered we capture it from onRowHover of the Table component and save it in the container state. This triggers a re-render of the entire list with really poor perfomance.
You can see a video of the issue here.
Here is a code sample:
import React from 'react'
import Avatar from 'material-ui/lib/avatar'
import Checkbox from 'material-ui/lib/checkbox'
import Table from 'material-ui/lib/table/table'
import TableHeaderColumn from 'material-ui/lib/table/table-header-column'
import TableRow from 'material-ui/lib/table/table-row'
import TableHeader from 'material-ui/lib/table/table-header'
import TableRowColumn from 'material-ui/lib/table/table-row-column'
import TableBody from 'material-ui/lib/table/table-body'
import R from 'ramda'
export default class ContactsList extends React.Component {
constructor (props) {
super(props)
this.state = { hoveredRow: 0 }
this.contacts = require('json!../../public/contacts.json').map((e) => e.user) // Our contact list array
}
_handleRowHover = (hoveredRow) => this.setState({ hoveredRow })
_renderTableRow = ({ hovered, username, email, picture }) => {
const checkBox = <Checkbox style={{ marginLeft: 8 }} />
const avatar = <Avatar src={picture} />
return (
<TableRow key={username}>
<TableRowColumn style={{ width: 24 }}>
{hovered ? checkBox : avatar}
</TableRowColumn>
<TableRowColumn>{username}</TableRowColumn>
<TableRowColumn>{email}</TableRowColumn>
</TableRow>
)
}
render = () =>
<Table
height='800px'
fixedHeader
multiSelectable
onRowHover={this._handleRowHover}
>
<TableHeader displaySelectAll enableSelectAll>
<TableRow>
<TableHeaderColumn>Nome</TableHeaderColumn>
<TableHeaderColumn>Email</TableHeaderColumn>
<TableHeaderColumn>Telefono</TableHeaderColumn>
</TableRow>
</TableHeader>
<TableBody displayRowCheckbox={false} showRowHover>
{this.contacts.map((contact, index) => this._renderTableRow({
hovered: index === this.state.hoveredRow,
...contact }))
}
</TableBody>
</Table>
}
Thank you in advance.
You could wrap your rows into a new component implementing shouldComponentUpdate like so :
class ContactRow extends Component {
shouldComponentUpdate(nextProps) {
return this.props.hovered !== nextProps.hovered || ...; // check all props here
}
render() {
const { username, email, ...otherProps } = this.props;
return (
<TableRow { ...otherProps } >
<TableRowColumn style={{ width: 24 }}>
{this.props.hovered ? checkBox : avatar}
</TableRowColumn>
<TableRowColumn>{this.props.username}</TableRowColumn>
<TableRowColumn>{this.props.email}</TableRowColumn>
</TableRow>
);
}
}
Then you can use it in your ContactList component like so :
this.contacts.map((contact, index) => <ContactRow key={contact.username} {...contact} hovered={index === this.state.hoveredRow} />)
If you don't want to manually implement shouldComponentUpdate, you can use React's PureRenderMixin or check a lib like recompose which provides useful helpers like pure to do so.
EDIT
As pointed out by the OP and #Denis, the approach above doesn't play well with some features of the Table component. Specifically, TableBody does some manipulation on its children's children. A better approach would be to define your ContactRow component like so:
class ContactRow extends Component {
shouldComponentUpdate(nextProps) {
// do your custom checks here
return true;
}
render() {
const { username, email, ...otherProps } = this.props;
return <TableRow { ...otherProps } />;
}
}
and then to use it like this
<ContactRow { ...myProps }>
<TableRowColumn>...</TableRowColumn>
</ContactRow>
But I guess having TableRow re-render only when necessary is a feature everyone would benefit from, so maybe a PR would be in order :)

Animated page transitions in react

The past couple of weeks I've been working on an app using React. So far everything is working fine, but now I want to add some transitions to it. These transitions are a bit more complex than any examples I managed to find.
I've got 2 pages, an overview and a detail page which I'd like to transition between.
I'm using react-router to manage my routes:
<Route path='/' component={CoreLayout}>
<Route path=':pageSlug' component={Overview} />
<Route path=':pageSlug/:detailSlug' component={DetailView} />
</Route>
Overview looks like this:
Detailview looks like this:
The idea of the transition is that you click on one of the elements of the Overview. This element which has been clicked moves towards the position it should have on the detailView. The transition should be initiated by a route change (I think) and should also be able to happen in reverse.
I've already tried using ReactTransitionGroup on the Layout, which has a render method which looks like this:
render () {
return (
<div className='layout'>
<ReactTransitionGroup>
React.cloneElement(this.props.children, { key: this.props.location.pathname })
</ReactTransitionGroup>
</div>
)
}
This will give the child component the ability to receive the special lifecycle hooks. But I'd like to access the child components somehow during these hooks and still keep doing things the React way.
Could someone point me in the right direction for the next step to take? Or maybe point me to an example which I may have missed somewhere? In previous projects I used Ember together with liquid fire to get these kinds of transitions, is there maybe something like this for React?
I'm using react/react-redux/react-router/react-router-redux.
Edit: Added a working example
https://lab.award.is/react-shared-element-transition-example/
(Some issues in Safari for macOS for me)
The idea is to have the elements to be animated wrapped in a container that stores its positions when mounted. I created a simple React Component called SharedElement that does exactly this.
So step by step for your example (Overview view and Detailview):
The Overview view gets mounted. Each item (the squares) inside the Overview is wrapped in the SharedElement with a unique ID (for example item-0, item-1 etc). The SharedElement component stores the position for each item in a static Store variable (by the ID you gave them).
You navigate to the Detailview. The Detailview is wrapped into another SharedElement that has the same ID as the item you clicked on, so for example item-4.
Now this time, the SharedElement sees that an item with the same ID is already registered in its store. It will clone the new element, apply the old elements position to it (the one from the Detailview) and animates to the new position (I did it using GSAP). When the animation has completed, it overwrites the new position for the item in the store.
Using this technique, it's actually independent from React Router (no special lifecycle methods but componentDidMount) and it will even work when landing on the Overview page first and navigating to the Overview page.
I will share my implementation with you, but be aware that it has some known bugs. E.g. you have to deal with z-indeces and overflows yourself; and it doesn't handle unregistering element positions from the store yet. I'm pretty sure if someone can spend some time on this, you can make a great little plugin out of it.
The implementation:
index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import Overview from './Overview'
import DetailView from './DetailView'
import "./index.css";
import { Router, Route, IndexRoute, hashHistory } from 'react-router'
const routes = (
<Router history={hashHistory}>
<Route path="/" component={App}>
<IndexRoute component={Overview} />
<Route path="detail/:id" component={DetailView} />
</Route>
</Router>
)
ReactDOM.render(
routes,
document.getElementById('root')
);
App.js
import React, {Component} from "react"
import "./App.css"
export default class App extends Component {
render() {
return (
<div className="App">
{this.props.children}
</div>
)
}
}
Overview.js - Note the ID on the SharedElement
import React, { Component } from 'react'
import './Overview.css'
import items from './items' // Simple array containing objects like {title: '...'}
import { hashHistory } from 'react-router'
import SharedElement from './SharedElement'
export default class Overview extends Component {
showDetail = (e, id) => {
e.preventDefault()
hashHistory.push(`/detail/${id}`)
}
render() {
return (
<div className="Overview">
{items.map((item, index) => {
return (
<div className="ItemOuter" key={`outer-${index}`}>
<SharedElement id={`item-${index}`}>
<a
className="Item"
key={`overview-item`}
onClick={e => this.showDetail(e, index + 1)}
>
<div className="Item-image">
<img src={require(`./img/${index + 1}.jpg`)} alt=""/>
</div>
{item.title}
</a>
</SharedElement>
</div>
)
})}
</div>
)
}
}
DetailView.js - Note the ID on the SharedElement
import React, { Component } from 'react'
import './DetailItem.css'
import items from './items'
import { hashHistory } from 'react-router'
import SharedElement from './SharedElement'
export default class DetailView extends Component {
getItem = () => {
return items[this.props.params.id - 1]
}
showHome = e => {
e.preventDefault()
hashHistory.push(`/`)
}
render() {
const item = this.getItem()
return (
<div className="DetailItemOuter">
<SharedElement id={`item-${this.props.params.id - 1}`}>
<div className="DetailItem" onClick={this.showHome}>
<div className="DetailItem-image">
<img src={require(`./img/${this.props.params.id}.jpg`)} alt=""/>
</div>
Full title: {item.title}
</div>
</SharedElement>
</div>
)
}
}
SharedElement.js
import React, { Component, PropTypes, cloneElement } from 'react'
import { findDOMNode } from 'react-dom'
import TweenMax, { Power3 } from 'gsap'
export default class SharedElement extends Component {
static Store = {}
element = null
static props = {
id: PropTypes.string.isRequired,
children: PropTypes.element.isRequired,
duration: PropTypes.number,
delay: PropTypes.number,
keepPosition: PropTypes.bool,
}
static defaultProps = {
duration: 0.4,
delay: 0,
keepPosition: false,
}
storeNewPosition(rect) {
SharedElement.Store[this.props.id] = rect
}
componentDidMount() {
// Figure out the position of the new element
const node = findDOMNode(this.element)
const rect = node.getBoundingClientRect()
const newPosition = {
width: rect.width,
height: rect.height,
}
if ( ! this.props.keepPosition) {
newPosition.top = rect.top
newPosition.left = rect.left
}
if (SharedElement.Store.hasOwnProperty(this.props.id)) {
// Element was already mounted, animate
const oldPosition = SharedElement.Store[this.props.id]
TweenMax.fromTo(node, this.props.duration, oldPosition, {
...newPosition,
ease: Power3.easeInOut,
delay: this.props.delay,
onComplete: () => this.storeNewPosition(newPosition)
})
}
else {
setTimeout(() => { // Fix for 'rect' having wrong dimensions
this.storeNewPosition(newPosition)
}, 50)
}
}
render() {
return cloneElement(this.props.children, {
...this.props.children.props,
ref: element => this.element = element,
style: {...this.props.children.props.style || {}, position: 'absolute'},
})
}
}
I actually had a similar problem, where I had a search bar and wanted it to move and wrap to a different size and place on a specific route (like a general search in the navbar and a dedicated search page). For that reason, I created a component very similar to SharedElement above.
The component expects as props, a singularKey and a singularPriority and than you render the component in serval places, but the component will only render the highest priority and animate to it.
The component is on npm as react-singular-compoment
And here is the GitHub page for the docs.

Resources