I passed an object to my component as a prop. I can access it and it returns it as an object but when I try to access an item out of the object which I'm sure it exist it says undefined.
When I run this:
import React, { Component } from 'react';
import { Text } from 'react-native';
import { CardSection } from './common';
class ListItem extends Component {
render() {
const { titleStyle } = styles;
console.log(this.props.library);
console.log(this.props.library.title);
return (
<CardSection>
<Text style={titleStyle}>{this.props.library.title}</Text>
</CardSection>
);
}
}
I get this:
{…}
index: 0
item: Object { id: 0, title: "Webpack", description: "Webpack is a module bundler. It packs CommonJs/AMD modules i. e. for the browser. Allows to split your codebase into multiple bundles, which can be loaded on demand." }
undefined
This result for the last console.log() is in the bottom.
This is the component from where I am passing the library object:
import React, { Component } from 'react';
import { FlatList } from 'react-native';
import { connect } from 'react-redux';
import ListItem from './ListItem';
class LibraryList extends Component {
renderItem(library) {
return <ListItem library={library} />;
}
render() {
return (
<FlatList
data={this.props.libraries}
renderItem={this.renderItem}
keyExtractor={library => library.id.toString()}
/>
);
}
}
const mapStateToProps = (state) => ({ libraries: state.libraries });
export default connect(mapStateToProps)(LibraryList);
Check the full code here: https://github.com/ceerees12/tech_stack
Try changing renderItem={this.renderItem} into renderItem={() =>this.renderItem(library)}
You're not passing title in the component, if you pass the prop title in your ListItem component then you will be able to access this.props.titleand if the title is in library prop then it should this.props.library.title.
Related
I have React Native project(with Expo). I'm trying to make my text selectable and add custom actions to context menu. After some googling I found react-native-selectable-text library which throws errors when I use SelectableText component.
This is my initial code:
class RfcItem extends Component {
static navigationOptions = {
title: "RfcItem",
...navStyles,
};
render() {
const { RFC, loading } = this.props;
if (loading) return null;
const { rfc: c } = RFC.content;
return (
<ScrollView contentContainerStyle={styles.container}>
{/* TODO: Make this text selectable */}
<Text category="h1" status="primary">
{RFC.content}
</Text>
</ScrollView>
);
}
This is the code with react-native-selectable-text:
import React, { Component } from "react";
import navStyles from "../../styles/navStyles";
import {
StyleSheet,
View,
ScrollView,
PureComponent,
TextInput,
} from "react-native";
import { graphql } from "react-apollo";
import gql from "graphql-tag";
import Markdown from "react-native-markdown-renderer";
import { instanceOf } from "prop-types";
import mdStyles from "../../styles/md";
import sharedStyles from "../../styles/shared";
import { Layout, Text } from "react-native-ui-kitten";
import SelectableText from "react-native-selectable-text";
class RfcItem extends Component {
static navigationOptions = {
title: "RfcItem",
...navStyles,
};
render() {
const { RFC, loading } = this.props;
if (loading) return null;
const { rfc: c } = RFC.content;
return (
<ScrollView contentContainerStyle={styles.container}>
<SelectableText
selectable
multiline
contextMenuHidden
scrollEnabled={false}
editable={false}
onSelectionChange={event => {
const {
nativeEvent: {
selection: { start, end },
},
} = event;
const str = text.substring(start, end);
onSelectionChange({ str, start, end });
}}
style={{
color: "#BAB6C8",
}}
value={"some text"}
/>
</ScrollView>
);
}
}
It throws an error: Tried to register two views with the same name RCTMultipleTextInputView
How can I add text selection(with context menu)? What is the best way to do it?
When I look into the react-native-selectable-text library, I can't see onSelectionChange props just onSelection. Also you have to items to display as you have not added menuItems props
So I started learning react-native from videos and they have used ListView but as the ListView will be deprecated soon and will be removed. I get to know that FlatList will be the proper replacement but being a beginner I am not able to migrate to Flatlist.
import React, { Component } from "react";
import { ListView } from 'react-native';
import { connect } from 'react-redux';
import ListItem from './ListItem';
class LibraryList extends Component {
componentWillMount() {
const ds = new ListView.DataSource({
rowHasChanged: (r1,r2) => r1 !==r2
});
this.dataSource =ds.cloneWithRows(this.props.libraries);
}
renderRow(library) {
return <ListItem library = { library } />;
}
render() {
return(
<ListView
dataSource = {this.dataSource}
renderRow = {this.renderRow}
/>
);
}
}
const mapStateToProps = state => {
return { libraries: state.libraries };
};
export default connect(mapStateToProps) (LibraryList);
Welcome to stackoverflow.
Migrating should be pretty straightforward, you don't need a dataSource anymore. You can pass your array of items directly to the Component.
import React, { Component } from "react";
import { FlatList } from 'react-native';
import { connect } from 'react-redux';
import ListItem from './ListItem';
class LibraryList extends Component {
renderRow({item}) {
return <ListItem library = { item } />;
}
render() {
return(
<FlatList
data = {this.props.libraries}
renderItem = {this.renderRow}
/>
);
}
}
const mapStateToProps = state => {
return { libraries: state.libraries };
};
export default connect(mapStateToProps) (LibraryList);
Header over to the documentation here to find out more.
I'm new to React Native (and React), and I'm trying to pass a function as a prop to a component.
My goal is to create a component where its onPress functionality can be set by the instantiator of the component, so that it is more reusable.
Here is my code so far.
App.js
import React, { Component } from 'react';
import { View } from 'react-native';
import TouchableButton from './components/touchable-button';
export default class App extends Component<Props> {
constructor () {
super();
}
handlePress () {
// this should be called when my custom component is clicked
}
render () {
return (
<View>
<TouchableButton handlePress={this.handlePress.bind(this)}/>
</View>
);
}
}
TouchableButton.js
import React, { Component } from 'react';
import { TouchableHighlight } from 'react-native';
import AppButton from "./app-button";
export default class TouchableButton extends Component {
handlePress;
constructor(props){
super(props);
}
render () {
return (
<TouchableHighlight onPress={
this.props.handlePress
}>
<AppButton/>
</TouchableHighlight>
);
}
}
I am passing the handlePress function as the prop handlePress. I would expect the TouchableButton's props to contain that function, however it isn't there.
Solution
Use arrow function for no care about binding this.
And I recommend to check null before calling the props method.
App.js
export default class App extends Component<Props> {
constructor () {
super();
}
handlePress = () => {
// Do what you want.
}
render () {
return (
<View>
<TouchableButton onPress={this.handlePress}/>
</View>
);
}
}
TouchableButton.js
import React, { Component } from 'react';
import { TouchableHighlight } from 'react-native';
import AppButton from "./app-button";
export default class TouchableButton extends Component {
constructor(props){
super(props);
}
handlePress = () => {
// Need to check to prevent null exception.
this.props.onPress?.(); // Same as this.props.onPress && this.props.onPress();
}
render () {
return (
<TouchableHighlight onPress={this.handlePress}>
<AppButton/>
</TouchableHighlight>
);
}
}
When writing handlePress={this.handlePress.bind(this)} you passing a statement execution ( which when and if executed returns a function). What is expected is to pass the function itself either with handlePress={this.handlePress} (and do the binding in the constructor) or handlePress={() => this.handlePress()} which passes an anonymous function which when executed will execute handlePress in this class context.
// Parent
handleClick( name ){
alert(name);
}
<Child func={this.handleClick.bind(this)} />
// Children
let { func } = this.props;
func( 'VARIABLE' );
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 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}