evaluating ( this.props.navigator ) Undefined is not an object - reactjs

I'm getting this error even though i'm passing in the navigator porp properly.
MySetup is in this way : Main navigator Page -> FirstView (onBtnPress) -> Details
I'm getting the error when i'm calling this.navigator.push in the firstView page.
Main File:
import React, { Component, PropTypes } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
Navigator
} from 'react-native';
class app extends Component{
constructor(props) {
super(props);
}
navigatorRenderScene(route, navigator) {
return <route.component navigator={navigator}/>
}
configureScene() {
return Navigator.SceneConfigs.VerticalDownSwipeJump;
}
render() {
return (
<Navigator
style={styles.container}
initialRoute= {{component: MainMapView}}
renderScene={this.navigatorRenderScene}/>
);
}
}
const styles = StyleSheet.create({
container: { flex: 1, flexDirection: 'column', padding: 20 }
});
AppRegistry.registerComponent('app', () => app);
First Component:
<ActionButton buttonColor="rgba(30,144,255,1)" style={styles.fabIcon}
onPress={this.fabPress}>
</ActionButton>
fabPress() {
this.props.navigator.push({
component : DetaislView
});
}
The error occurs on fabPress.
Any ideas on what i'm doing wrong?

try this in your FirstComponent.js:
class FirstComponent extends Component {
constructor(props) {
super(props);
this.fabPress = this.fabPress.bind(this);
}
// ... rest of the code remains the same
Why we had to do this?
Back in time when we were using React.createClass (ES5), class methods were automatically bound to the class. But when we started to extend (ES6 classes), we need to bind the methods explicitly to the class env.
fabPress being passed as an event's callback function, it is executed in another env outside the class; hence the this will be coming from the scope of execution env. But we need this of our class to access this.props.navigator :)

Just in case if anyone is interested in why this doesn't work even if you are having the function inside the class.
The function isn't binded to the class if declared as shown in the following.
theFunctionName() {
// your code
}
The function is binded to the class if it is declared with the following syntax.
theFunctionName = () => {
// your code
}
thus you can omitt the bind(this) code.
reference taken from here
Please make sure you have the necessary presets. Since the arrow function is highly experimental feature (as of this period in time)

For my case I passed in the navigator:
onPress={this.fabPress(navigator)}
fabPress(navigator) {
navigator.push({component: DetaislView});
}

Related

What is a useless Constructor in React?

I have two files
Description.js and
subjects.js
Subject.js file contains an array of subjects
export const Subjects=[
{
id:1,
title:"Mathematics",
text:"Cheat Sheet for Mathematics",
img:"./Images/math.jpg",
},
{
id:2,
title:"C-programming",
text:"Cheat Sheet for C-Programming",
img:"./Images/cprog.jpg",
},
{
id:3,
title:"Physics",
text:"Cheat Sheet for Physics",
img:"./Images/physics.jpg",
},
{
id:4,
title:"Youtube",
text:"Recomended Youtube videos for Learning",
img:"./Images/youtube.jpg",
},
]
I want to use this array in Description.js .I'm using a map function
import React, { Component } from 'react';
import {Subjects} from './subjects'
class Description extends Component{
constructor(props){
super(props);
}
render(){
const description =this.props.Subjects.map((subjects)=>{
return(
<h1>{subjects.title}</h1>
)
})
return(
{description}
)
}
}
export default Description;
But I am recieving an error of
TypeError: Cannot read property 'map' of undefined
Also in my vs code terminal.I have these mentioned
Line 2:9: 'Subjects' is defined but never used no-unused-vars
Line 5:5: Useless constructor no-useless-constructor
A "useless constructor" is one that the linter is warning you can safely remove from the code, because it doesn't accomplish anything - if all you have is a super call (with the same argument the class is created with), the constructor doesn't do anything useful, because classes will already call super automatically, if the constructor isn't given:
class Parent {
constructor(name) {
console.log('parent constructor running', name);
}
}
class Child extends Parent {}
const c = new Child('bob');
So, the linter is telling you to delete the following lines:
constructor(props){
super(props);
}
Since the Subjects identifier isn't being used anywhere, it can be removed too. Delete the line:
import {Subjects} from './subjects'
#CertainPerformance answers what a useless constructor is well: basically an ESLint rule that doesn't allow you to have constructors that do nothing.
This should work for what you want to happen, though if this is the extent of the component I would place it in a functional component. In order to use this Subject array as a prop you'd have to import it in another component and pass to Description like <Description subjects={Subjects} />
import React, { Component } from 'react';
import { Subjects } from './subjects';
class Description extends Component {
render() {
const description = Subjects.map(subjects => {
return <h1>{subjects.title}</h1>;
});
return <div>{description}</div>;
}
}
export default Description;
Or as a functional component:
import React from 'react';
import { Subjects } from './subjects';
const Description = () => {
const description = Subjects.map(subjects => {
return <h1>{subjects.title}</h1>;
});
return <div>{description}</div>;
};
export default Description;

Simplify this repeating code pattern on class components

I'm currently implementing accessibility (VoiceOver/Talkback) support for my application, and I use AccessibilityInfo.setAccessibilityFocus (see official docs) quite a lot which requires a reactTag, which I can only get by using findNodeHandle as per this answer.
This means that I keep repeating the same pattern involving quite a few function calls, over and over on many different components. I originally tried to move the stored references and the call to set focus to my state manager (using MobX in this case) but I ended up getting a lot of failed findNodeHandle calls because sometime the component had unmounted before the call was made.
This is the gist of what I keep repeating:
import React, {Component} from 'react'
import {
...
findNodeHandle,
AccessibilityInfo
...
} from 'react-native';
class Sample extends React.Component {
constructor(props) {
super(props)
this.accessibilityRef = null;
}
...
componentDidMount() {
this.setAccessibilityFocus()
}
componentDidUpdate() {
this.setAccessibilityFocus()
}
setAccessibilityRef(el) {
this.accessibilityRef = el
}
setAccessibilityFocus() {
if (this.accessibilityRef) {
const reactTag = findNodeHandle(this.accessibilityRef);
AccessibilityInfo.setAccessibilityFocus(reactTag);
}
}
render() {
return (
<View ref={this.setAccessibilityRef} accessible={true}>
...
</View>
)
}
}
Would it be possible to somehow make something reusable out of this? Maybe a decorator or as a class extension so that I can reuse it?
Yes, you can...
Let, SampleWrapper.js is your wrapper.
import React, {Component} from 'react'
import {
...
findNodeHandle,
AccessibilityInfo
...
} from 'react-native';
export default class SampleWrapper extends Component {
constructor(props) {
super(props)
this.accessibilityRef = null;
}
...
componentDidMount() {
this.setAccessibilityFocus()
}
componentDidUpdate() {
this.setAccessibilityFocus()
}
setAccessibilityRef(el) {
this.accessibilityRef = el
}
setAccessibilityFocus() {
if (this.accessibilityRef) {
const reactTag = findNodeHandle(this.accessibilityRef);
AccessibilityInfo.setAccessibilityFocus(reactTag);
}
}
render() {
return (
<View ref={this.setAccessibilityRef} accessible={true}>
{this.props.children}
</View>
)
}
}
Now, assume you want to use the above wrapper in your Sample.js
import React, {Component} from 'react'
import SampleWrapper from './path/to/SampleWrapper'
class Sample extends Component {
constructor(props) {
super(props)
}
render() {
return (
<SampleWrapper>
....
</SampleWrapper>
)
}
}
You can put ref s and then you can control it as you expect.
PS: I didn't test this before posting. Hope this will work and help you. If you have any problems regarding this answer or if you want to add something more and you have doubts about how to do it, please just comment on it here.

return from navigation in react native give me undefined in this.props.navigation.state.params.filePath

i need help :).
my project is 2 pages in react native, MainPage and SoundRecord.
my init screen is MainPage and when i press the button 'take sound'
i move to another component to record sound(i move with react native navigation).
when i come back i want to return the filePath(where it save the file..).
i want to insert it to the state.
when i do this in MainPage:
this.state{
filePath: this.props.navigation.state.params.filePath
}
it give error:
undefined is not an object(evaluating 'this.props.navigation.state.params.filePath')
and i understand this because i start my project with MainPage and i dont have the filePath from the SoundRecord page.
what can i do?
can i do check if this.props.navigation.state.params.filePath !== undefined?
how to do it? i try almost everything...
i put the relevant code:
MainPage:
import React, { Component } from 'react';
import { Platform, StyleSheet, Text, View, TouchableOpacity } from 'react-native';
import ImagePicker from 'react-native-image-picker';
import { RNS3 } from 'react-native-aws3';
import { aws } from './keys';
import SoundRecord from './SoundRecord'
export default class MainPage extends Component {
constructor(props){
super(props);
this.state={
file:'' ,
config:'',
filePath: '',
fileName: '',
tag:''
};
}
MoveToSoundRecordPage = () => {
this.props.navigation.navigate('SoundRecord');
}
render() {
return (
<View style={styles.container}>
<TouchableOpacity onPress={this.MoveToSoundRecordPage}>
<Text>take sound</Text>
</TouchableOpacity>
{/* <SoundRecord takeSound={this.takeSound}/> */}
<Text>{this.state.fileName}</Text>
<TouchableOpacity onPress={this.UploadToAWS.bind(this)}>
<Text>Upload To Aws</Text>
</TouchableOpacity>
</View>
);
}
}
SoundRecord when i finish to record i send the filePath like this:
finishRecord = (filePath) => {
this.props.navigation.navigate('MainPage',{filePath});
}
thank!
If you want to update something on the previous page then you can do it in the following way.
Create a function in the MainPage that updates the state with the new filepath.
Pass the function as as a param.
Use the passed function in SoundRecord to update the state in the MainPage
MainPage
In your MainPage add the following
sendFilepath = (filePath) => {
this.setState({filePath})
}
Update the MoveToSoundRecordPage function to take the sendFilepath as a parameter:
MoveToSoundRecordPage = () => {
this.props.navigation.navigate('SoundRecord', { sendFilepath: this.sendFilepath });
}
SoundRecord
Then in the SoundRecord page you want to update the finishRecord so that it calls the function that you have passed.
finishRecord = (filePath) => {
this.props.navigation.state.params.sendFilepath(filePath)
this.props.navigation.goBack();
}
Snack
Here is a snack https://snack.expo.io/#andypandy/navigation-passing-function that shows passing a function called sendFilepath from Screen1 to Screen2. Then in Screen2 it then calls the function that was passed and this updates the state in Screen1.
Please try this. It may help you
Add this in MainPage screen:
componentWillMount() {
const filePath = this.props.navigation.getParam('filePath', '');
this.setState({ filePath:filePath })
}

Proper way of defining/initializing state in reactjs or react-native

So far I understand there are two ways to define state in react class.
The first as many people use them, is as follows:
import React, { Component } from "react";
import { View, Text } from "react-native";
export default class Test extends Component {
constructor (props) {
super(props)
this.state = {
text: 'hello'
}
}
render() {
return (
<View>
<Text>{this.state.text}</Text>
</View>
);
}
}
The second one is as follows:
import React, { Component } from "react";
import { View, Text } from "react-native";
export default class Test extends Component {
state = {
text: "hello"
}
render() {
return (
<View>
<Text>{this.state.text}</Text>
</View>
);
}
}
The difference is at using constructor or not. What is the effect and is there any difference at all between the two? If there is, which one should I use?
Thank you!
Both methods are correct. Make sure you have support for class properties enabled in the babelrc. If you are using CRA both will work. Constructor one is better on the eyes if you want to seed the initial state from props.
Both methods are fine. Second one is the short-hand method

Passing React Navigation to Child of Child Component

I'm dynamically building my "screen" with the use of child "row" and "button" components. I'm only using this method because I can't find a flex-flow property available for react-native.
So basically I'm mapping through an array of arrays to build each row, and within the row, mapping through each array to build each button. Because the onPress needs to be set in the button, I'm passing the URL for each
onPress{() => this.props.navigation.navigate({navigationURL})
as a prop, first to the row, and then to the button. The problem is I keep getting the error 'Cannot read property 'navigation' of undefined. I'm sure this is because only the actual "screens" within the navigator have access to the navigation props. I've also tried passing
navigation={this.props.navigation}
but had no success. I've looked through all of the documentation and can't seem to find anything helpful. Anyone else encountered a similar situation?
If you want to access the navigation object from a component which is not part of navigator, then wrap that component in withNavigation HOC. Within the wrapped component you can access navigation using this.props.navigation. Take a look at the official document
Sample
import { withNavigation } from 'react-navigation';
...
class CustomButton extends React.Component {
render() {
return <Button title="Back" onPress={() => {
this.props.navigation.goBack() }} />;
}
}
export default withNavigation(CustomButton);
Hope this will help!
Ahhh, silly mistake. I wasn't setting up Props in the constructor. Thank you Prasun Pal for the help! Here's my code if someone else has an issue.
import React, { Component } from 'react'
import { Image, Text, TouchableOpacity, View } from 'react-native'
import { withNavigation } from 'react-navigation'
class ButtonName extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return (
<TouchableOpacity
onPress={() => this.props.navigation.navigate('PageName')}
>
</TouchableOpacity>
)
}
}
export default withNavigation(ButtonName);

Resources