I don't understand how I'm getting this error (pic below). In my LoginForm.js file, the onEmailChange(text) is giving me an unresolved function or method call to onEmailChange() error when I hover over it in my WebStorm IDE. In my index.js file, no error is being thrown anywhere.
I've looked around SO for this issue but it doesn't fully pertain to my problem.
I've tried File > Invalidate Caches/Restart but that didn't work.
Here's App.js:
import React, { Component } from 'react';
import {StyleSheet} from 'react-native';
import {Provider} from 'react-redux';
import {createStore} from 'redux';
import firebase from 'firebase';
import reducers from './reducers';
import LoginForm from './components/common/LoginForm';
class App extends Component {
render() {
return(
<Provider style={styles.c} store={createStore(reducers)}>
<LoginForm/>
</Provider>
);
}
}
const styles = StyleSheet.create({
c: {
flex: 1
}
});
export default App;
Here's LoginForm.js:
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {emailChanged} from 'TorusTeensApp/src/actions';
import {Text, StyleSheet, KeyboardAvoidingView, TextInput, TouchableOpacity} from 'react-native';
class LoginForm extends Component {
render() {
onEmailChange(text)
{
this.props.emailChanged(text);
}
return(
<KeyboardAvoidingView style={styles.container}>
<TextInput
style={styles.userInput}
onsubmitediting={() => this.passwordInput.focus()}
returnKeyType={"next"}
placeholder={"Email"}
label={"Email"}
keyboardType={"email-address"}
autoCorrect={false}
onChangeText={this.onEmailChange.bind(this)}
value={this.props.email}
/>
<TextInput
style={styles.userInput}
ref={(userInput) => this.passwordInput = userInput}
returnKeyType={"go"}
placeholder={"Password"}
label={"Password"}
secureTextEntry
/>
<TouchableOpacity style={styles.buttonContainer}>
<Text style={styles.buttonText}>Login</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.buttonContainer}>
<Text style={styles.buttonText}>Create Account</Text>
</TouchableOpacity>
</KeyboardAvoidingView>
);
}
}
const styles = StyleSheet.create({
container: {
padding: 20 // creates a gap from the bottom
},
userInput: {
marginBottom: 20,
backgroundColor: '#9b42f4',
height: 40
},
buttonContainer: {
backgroundColor: '#41bbf4',
paddingVertical: 10,
marginBottom: 20
},
buttonText: {
textAlign: 'center',
color: '#FFFFFF'
}
});
const mapStateToProps = state => {
return {
email: state.auth.email
};
};
export default connect(mapStateToProps, null, {emailChanged}) (LoginForm);
Here's index.js:
import {EMAIL_CHANGED} from './types';
export const emailChanged = (text) => {
return {
type: 'EMAIL_CHANGED',
payload: text
};
};
export default emailChanged();
Your connect is miswired
connect(mapStateToProps, null, {emailChanged}) (LoginForm);
It should be something like:
connect(mapStateToProps,
(dispatch) => ({emailChanged: (text) => dispatch(emailChanged(text))})
)(LoginForm);
so that your action actually gets dispatched
and as spotted by emed in comment:
export default emailChanged;
without parentheses.
You defined your callback inside your render() method and not inside the class body. Do it like this:
class LoginForm extends Component {
onEmailChange(text) {
this.props.emailChanged(text);
}
render() {
return(...);
}
}
Also you shouldn't bind methods inside your render() method. Do it in the constructor of your Component:
class LoginForm extends Component {
constructor(props) {
super(props);
this.onEmailChange.bind(this);
}
onEmailChange(text) {
// do something
}
// other methods
}
Or if you use babel and ES6, you can define your callback with an arrow function, then it will be automatically bound:
class LoginForm extends Component {
onEmailChange = text => {
// do something
};
// other methods
}
See also the react docs about autobinding.
Also your call to connect seems incorrect. If you want to dispatch the action emailChanged it has to look like this:
const mapStateToProps = state => {
return {
email: state.auth.email
};
};
const mapDispatchToProps = dispatch => {
// this put a function emailChanged into your props that will dispatch the correct action
emailChanged: text => dispatch(emailChanged(text))
};
const LoginFormContainer = connect(mapStateToProps, mapDispatchToProps)(LoginForm);
export default LoginFormContainer;
The third argument to connect needs to be a function that knows how to merge the output of mapStateToProps, mapDispatchToProps, and ownProps all into one object that is then used as props for your connected component. I think you're trying to pass that action to the mapDispatchToProps argument, which is the second argument not the third. So, based on what I think you're doing, you probably wanna change your connect line to look like this.
export default connect(mapStateToProps, {emailChanged}) (LoginForm);
Then, export the function from your actions file not the output of calling that function.
export default emailChanged;
Notice I removed the parentheses so it's not being called.
Then make the callback function a method on your class and bind it in the constructor.
constuctor(props) {
super(props);
this.onEmailChange = this.onEmailChange.bind(this);
}
onEmailChange(text) {
this.props.emailChanged(text);
}
Then update onChangeText on that element.
onChangeText={this.onEmailChange}
Related
I am trying to programatically focus TextInput element with a certain delay after it has mounted, I've got following component (a view that shows input and button)
For some reason this gets error saying
_this2.inputRef.focus is not a function
I'm not sure why. One out of place thing is that I get flow saying that createRef() doesn't exist on React, but I am assuming this is just missing flow definition now, as I am using react 16.3.1 and this was added in 16.3, plus there is no error when its called.
// #flow
import React, { Component, Fragment } from 'react'
import Button from '../../composites/Button'
import TextInput from '../../composites/TextInput'
import OnboardingStore from '../../store/OnboardingStore'
import withStore from '../../store'
import { durationNormal } from '../../services/Animation'
/**
* Types
*/
export type Props = {
OnboardingStore: OnboardingStore
}
/**
* Component
*/
class CharacterNameView extends Component<Props> {
componentDidMount() {
this.keyboardTimeout = setTimeout(() => this.inputRef.focus(), durationNormal)
}
componentWillUnmount() {
clearTimeout(this.keyboardTimeout)
}
keyboardTimeout: TimeoutID
inputRef = React.createRef()
render() {
const { OnboardingStore } = this.props
return (
<Fragment>
<TextInput
ref={this.inputRef}
enablesReturnKeyAutomatically
value={OnboardingStore.state.username}
onChangeText={username => OnboardingStore.mutationUsername(username)}
placeholder="Username"
blurOnSubmit
returnKeyType="done"
onSubmitEditing={/* TODO */ () => null}
/>
<Button disabled={!OnboardingStore.state.username} color="GREEN" onPress={() => null}>
Create
</Button>
</Fragment>
)
}
}
export default withStore(OnboardingStore)(CharacterNameView)
TextInput component I use is imported from this file
// #flow
import React, { Component } from 'react'
import { StyleSheet, TextInput as Input } from 'react-native'
import RatioBgImage from '../components/RatioBgImage'
import { deviceWidth } from '../services/Device'
/**
* Types
*/
export type Props = {
style?: any
}
/**
* Component
*/
class TextInput extends Component<Props> {
static defaultProps = {
style: null
}
render() {
const { style, ...props } = this.props
return (
<RatioBgImage source={{ uri: 'input_background' }} width={70} ratio={0.1659}>
<Input
{...props}
placeholderTextColor="#4f4a38"
selectionColor="#797155"
autoCapitalize="none"
autoCorrect={false}
keyboardAppearance="dark"
style={[styles.input, style]}
/>
</RatioBgImage>
)
}
}
export default TextInput
/**
* Styles
*/
const styles = StyleSheet.create({
input: {
width: '96.4%',
height: '97.45%',
color: '#797155',
fontSize: deviceWidth * 0.043,
marginLeft: '1%',
paddingLeft: '5%'
}
})
Below is what this.innerRef looks like, I don't see any focus property on it at the moment
inputRef is a property of the class instance, set it inside a class method.
Using a constructor, for instance:
class CharacterNameView extends Component<Props> {
constructor() {
this.inputRef = React.createRef()
}
...
}
My issue was in using React.createRef() and assuming I am setting it on a child component. Unfortunately I missed part of the docs about React.forwardRef That I had to use in a child component so ref gets set properly.
I'm not sure how to describe what I'm trying to do with words so please take a look at the following code:
This is what causing me issues: this.fetchMessages()
import React, { Component } from 'react';
import { PushNotificationIOS, FlatList, TextInput, ActivityIndicator, ListView, Text, View, Image, TouchableWithoutFeedback, AsyncStorage } from 'react-native';
import { Actions } from 'react-native-router-flux';
import ConversationsItem from './ConversationsItem';
import { conversationFetch } from '../actions';
import { connect } from 'react-redux';
import { Divider } from 'react-native-elements'
import PushNotification from 'react-native-push-notification';
class Conversations extends Component {
componentDidMount() {
this.props.conversationFetch()
}
fetchMessages() {
this.props.conversationFetch()
}
render() {
PushNotification.configure({
onNotification: function(notification) {
PushNotification.getApplicationIconBadgeNumber((response) => {
PushNotification.setApplicationIconBadgeNumber(response + 1)
})
console.log( 'NOTIFICATION:', notification )
notification.finish(PushNotificationIOS.FetchResult.NoData);
this.fetchMessages()
}
});
if (!this.props.email) {
return (
<View style={{flex: 1, paddingTop: 20}}>
<ActivityIndicator />
</View>
);
}
console.log(this.props.conversations)
return (
<View style={{flex: 1, backgroundColor: 'white'}}>
...
</View>
);
}
}
const mapStateToProps = (state) => {
console.log(state)
const { conversations } = state.conversation;
const { email } = state.conversation;
return { conversations, email };
};
export default connect(mapStateToProps, { conversationFetch })(Conversations);
When I call this.fetchMessages() inside PushNotification.configure({}), I get the following error message:
this.fetchMessages is not a function
I'm not sure if what I'm doing is possible but if so I'd really like to know how to make this work.
I tried adding .bind(this) and other ways around but got same error anyways.
Thanks for you help.
Functions declared with function keyword has their own this, so this inside onNotification function does not refer to the class.
Therefore use arrow function syntax, which will lexically resolve this and value of this inside will refer to class itself. So convert
onNotification: function(notification) {
to
onNotification: (notification) => {
So you have in fact tried binding the fetchMessages function in the constructor? Like such:
constructor(props) {
super(props)
this.fetchMessages = this.fetchMessages.bind(this);
}
You can also use an arrow function to bind your method to the class without calling the constructor like such:
() => this.fetchMessages()
I think I'm close with setting up React Redux for my React native app. I currently have this set up.
Here I define the action that I want to call.
/* actions/mapActions.js */
export const setMarker = selectedMarker => {
return {
type: 'SET_MARKER',
selectedMarker
}
}
Here I define the container for the component that I want to use the store in.
//containers/mapContainers.js
import { connect } from 'react-redux';
import { setMarker } from './actions/mapActions'
import HomeScreen from './screens/HomeScreen'
const mapStateToProps = state => {
return {
selectedMarker: state.marker
}
}
const mapDispatchToProps = dispatch => {
return {
markerClick: (marker) => {
dispatch(setMarker(marker))
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(HomeScreen)
Here I combine the reducers as I've seen in a tutorial I've been following.
//reducers/index.js
import { combineReducers } from 'redux'
import mapReducer from './mapReducer'
const dabApp = combineReducers({
mapReducer
})
export default dabApp
Here I define the reducer for the component.
//reducers/mapReducers.js
const mapReducer = (state = [], action) => {
switch (action.type) {
case 'SET_MARKER':
return [
...state,
{
marker: action.marker
}
]
default:
return state
}
}
export default mapReducer
Main entry point to the application.
//App.js
// other imports here
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import snapApp from './reducers';
let store = createStore(dabApp);
export default class App extends React.Component {
state = {
isLoadingComplete: false,
};
render() {
return (
<Provider store={store}>
<View style={styles.container}>
{Platform.OS === 'ios' && <StatusBar barStyle="default" />}
{Platform.OS === 'android' &&
<View style={styles.statusBarUnderlay} />}
<RootNavigation />
</View>
</Provider>
);
}
}
const styles = StyleSheet.create({
//Styles.
});
Here I define the component.
//Standard react imports.
import { MapView } from 'expo';
import { connect } from 'react-redux';
export default class HomeScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
//Set states.
};
}
render() {
return (
<View style={styles.container}>
<MapView
//MapView info
>
{this.state.markers.map((marker) =>
<MapView.Marker
key={marker.id}
coordinate={marker.coordinate}
onPress={() => {this.props.markerClick(marker); this.props.navigation.navigate('Information');}}>
</MapView.Marker>
)}
</MapView>
</View>
);
}
}
const styles = StyleSheet.create({
//Styles.
});
The error I get is that the function 'markerClick' is undefined in the Map.Marker onPress prop. I have followed the tutorial religiously and can't find the solution to this.
The tutorial that I was following was this one on the official redux site. http://redux.js.org/docs/basics/ExampleTodoList.html.
Has anyone encountered the same issue?
Unfortunately, Harry's answer hasn't solved the issue.
I console.log(this.props) and I get this:
Still is undefined. When I console.log(this.props") I get:
Object {
"navigation": Object {
"dispatch": [Function anonymous],
"goBack": [Function goBack],
"navigate": [Function navigate],
"setParams": [Function setParams],
"state": Object {
"key": "Home",
"routeName": "Home",
},
},
"screenProps": undefined,
"selectedMarker": [Function dispatch],
"type": "SET_MARKER",
}
so I don't even see the function on my props.
As you can see, the function is not defined on this.props.
Thanks,
I feel like you're making more steps than needed.
Try something like this:
import React, { Component } from 'react';
import { MapView } from 'expo';
import { connect } from 'react-redux';
import { View, StyleSheet } from 'react-native';
import { setMarker } from './actions/mapActions'
class HomeScreen extends Component {
onPress(marker) {
this.props.setMarker(marker);
this.props.navigation.navigate('Information');
}
render() {
return (
<View style={styles.container}>
<MapView>
{this.state.markers.map((marker) => (
<MapView.Marker
key={marker.id}
coordinate={marker.coordinate}
onPress={() => { this.onPress(marker); }}
/>
)
)}
</MapView>
</View>
);
}
}
const styles = StyleSheet.create({
//Styles.
});
export default connect(null, setMarker)(HomeScreen);
You don't need to define a function that then dispatches an action, you can just connect the action to the component.
It's also much nicer to just have everything in the same file instead of having a separate mapContainers.js
I'm trying to create a route to a screen by pressing a button in a side menu using react navigation. When I try to implement it, I get the following error:
undefined is not an object (evaluating '_this.props.user.Range')
The error flags, point something wrong in here:
RangeValues: this.props.user.Range
The rest of the components I am trying to navigate to are here:
import React, {Component} from 'react'
import {
StyleSheet,
View,
Text,
Switch,
} from 'react-native'
import Slider from 'react-native-multislider'
export default class Profile extends Component {
state = {
RangeValues: this.props.user.Range,
distanceValue: [this.props.user.distance]
}
render() {
const {name, work, id} = this.props.user
const {RangeValues, distanceValue} = this.state
return (
<View style={styles.container}>
<View style={styles.profile}>
<Text style={{fontSize:20}}>{name}</Text>
</View>
<View style={styles.label}>
<Text>Distance</Text>
<Text style={{color:'darkgrey'}}>{distanceValue}km</Text>
</View>
<Slider
min={1}
max={100}
values={distanceValue}
onValuesChange={val => this.setState({distanceValue:val})}
onValuesChangeFinish={val => this.updateUser('distance', val[0])}
/>
<View style={styles.label}>
<Text>Age Range</Text>
<Text style={{color:'darkgrey'}}>{ageRangeValues.join('-')}</Text>
</View>
<Slider
min={1}
max={200}
values={RangeValues}
onValuesChange={val => this.setState({RangeValues:val})}
onValuesChangeFinish={val => this.updateUser('Range', val)}
/>
</View>
)
}
}
I want to navigate from a button in a drawer using stack navigator, my routing is as follows:
import { StackNavigator } from 'react-navigation';
import React from 'react';
import Home from './screens/home';
import Login from './screens/login';
import Profile from './screens/profile';
const RouteConfigs = {
Login: { screen: Login },
Home: { screen: Home },
Profile: { screen: Profile },
};
const StackNavigatorConfig = {
headerMode: 'none',
}
export default StackNavigator(RouteConfigs, StackNavigatorConfig)
Where I am trying to navigate from is the Home screen component. The function I have used for this within the Home component is:
() => this.props.navigation.navigate('Profile', { user: this.state.user })
Does anybody know how to fix this error in order for me to navigate from the home component to the profile component?
I think you should bind your props from constructor :
export class MyClass extends Component {
constructor(props) {
super(props);
if (!this.state) {
this.state = {
data: props.myData
}
}
render() {DO THE JOB HERE}
}
}
Then you can access to this.state instead of state
For more explanations, props are not yet available since the constructor has not pass. See Component doc : https://facebook.github.io/react/docs/react-component.html#constructor
I'm following a course on react-native and I just ran into this error when trying to add a modal component to a page:
Element type is invalid: expected a string(for built-in components) or a class/function (for composite components) but got: undefined.
Here's my code:
EmployeeEdit.js
import _ from 'lodash';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Communications from 'react-native-communications';
import { employeeUpdate, employeeSave, employeeReset } from '../actions';
import { Card, CardSection, Button, Confirm } from './common';
import EmployeeForm from './EmployeeForm';
class EmployeeEdit extends Component {
constructor(props) {
super(props);
this.state = { showModal : false};
this.onButtonPress = this.onButtonPress.bind(this);
this.onTextPress = this.onTextPress.bind(this);
}
componentWillMount() {
_.each(this.props.employee, (value, prop) => {
this.props.employeeUpdate({prop, value});
});
}
componentWillUnmount() {
this.props.employeeReset();
}
onButtonPress(){
const { name, phone, shift } = this.props;
this.props.employeeSave({ name, phone, shift, uid: this.props.employee.uid });
}
onTextPress(){
const { name, phone, shift } = this.props;
Communications.text(phone, `Hello ${name}, your upcoming shift is on ${shift}`);
}
render () {
return (
<Card>
<EmployeeForm {...this.props} />
<CardSection>
<Button onPress={this.onButtonPress}>
Save Changes
</Button>
</CardSection>
<CardSection>
<Button onPress={this.onTextPress}>
Text Schedule
</Button>
</CardSection>
<CardSection>
<Button onPress={()=> this.setState({ showModal: !this.state.showModal })}>
Fire Employee
</Button>
</CardSection>
<Confirm
visible={this.state.showModal}
>
Are you sure you want to fire this employe?
</Confirm>
</Card>
);
}
}
const mapStateToProps = (state) => {
const {name, phone, shift} = state.employeeForm;
return {name, phone, shift};
};
export default connect(mapStateToProps, {
employeeUpdate,
employeeSave,
employeeReset
})(EmployeeEdit);
Confirm.js
import React from 'react';
import { Text, View, Modal } from 'react-native';
import { CardSection } from './CardSection';
import { Button } from './Button';
const Confirm = ({ children, visible, onAccept, onDecline }) => {
const { containerStyle, textStyle, cardSectionStyle } = styles;
return (
<Modal
visible={visible}
transparent
animationType='slide'
onRequestClose={() => {}}
>
<View style={containerStyle}>
<CardSection style={cardSectionStyle}>
<Text style={textStyle}>
{children}
</Text>
</CardSection>
<CardSection>
<Button onPress={onAccept}>Yes</Button>
<Button onPress={onDecline}>No</Button>
</CardSection>
</View>
</Modal>
);
};
const styles = {
cardSectionStyle: {
justifyContent: 'center'
},
textStyle: {
flex: 1,
fontSize: 18,
textAlign: 'center',
lineHeight: 40
},
containerStyle: {
backgroundColor: 'rgba(0, 0, 0, 0.75)',
position: 'relative',
flex: 1,
justifyContent: 'center'
}
};
export default { Confirm };
The error only occurs when I add the confirm component to EmployeeEdit. When I remove the confirm component at the bottom the error goes away. Is there some error in my confirm component?
Thanks
at the bottom of Confirm.js it should be
export { Confirm };
not
export default { Confirm };
When you're importing Confirm using curly braces
import { Card, CardSection, Button, Confirm } from './common';
make sure you're not exporting it using default like what you did:
export default { Confirm };
because once you use
default syntax, it doesn't require curly braces syntax when importing.
You can avoid the flower braces in all.
Do this.
In your Confirm.js
export default Confirm
In your EmployeeEdit.js
import Confirm from './Confirm'
As you see, I have omitted the braces. According to the ES6 Documentation, if a module defines a default export, then you can import the default export without the use of curly braces. Refer here: What is "export default" in javascript?