So if i have alot of different props for one component I wish i could do something like
const { ...props } = props;
instead of
const { prop1, prop2, prop3, ... } = props;
why isn't statement 1 valid? or am I doing the syntax wrong?
Edit: statement 1 is valid i see that was me being stupid now.
what im looking for is a programatic way to achieve statement 2 so i dont have to write out loads of props
option one is useful if you are using HOCs, for example in React native, if you are making animated components you can go:
import React from 'react';
import { Animated, Text} from 'react-native';
class ComponentToBeAnimated extends React.Component {
render() {
const {...other} = this.props;
return (
<Text {...other}>
hello world
</Text>
)
}
}
export const AnimatedText = Animated.createAnimatedComponent(ComponentToBeAnimated )
Then when you instantiate AnimatedText, you can apply styles as if you were instantiating Text
import React from 'react';
import { StyleSheet, View} from 'react-native';
import { AnimatedText } from './AnimatedText';
const styles = StyleSheet.create({
title: {
fontSize: 19,
fontWeight: 'bold',
},
});
export const Message = () => {
return (
<View>
<AnimatedText style={styles.title}/>
</View>
)
}
See you can apply all the Text props you want from the HOC
Related
I have a real doozy here and I can't wrap my head around it.
I have this component:
<TouchableOpacity
onPress={() => {
if (myContext.value) {
alert('true');
//changes myContext.value to false
} else {
alert('false');
//changes myContext.value to true
}
}}>
<Text>{myContext.value ? 'working' : 'not working'}</Text>
</TouchableOpacity>
The weird thing that I don't understand is that the context IS changing the onPress function. So if I click the button it will alert true at first and then false and back and forth as expected. What's not changing is the text that should be going between "working" and "not working".
This component is definitely within the scope of my Provider. I'm using useContext to get access to myContext.
Does anyone know why this might be happening?
The point to be noted here is that just changing myContext.value = false or true Will not trigger a re-render.
I am not sure how are you handling the value change, But I've created a replica of what you want
Check out this Snack to see the working example.
Create a Context as shown below
import { createContext } from 'react';
const MyContext = createContext();
export default MyContext;
An example App.js component would look like
import * as React from 'react';
import { Text, View, TouchableOpacity } from 'react-native';
import Constants from 'expo-constants';
import MyContext from './MyContext';
import MyComponent from './MyComponent';
export default function App() {
const [value, setValue] = React.useState(true);
return (
<MyContext.Provider value={{ value, setValue }}>
<MyComponent />
</MyContext.Provider>
);
}
And your component where you want to perform this operation should look like this
import * as React from 'react';
import { Text, View, StyleSheet, TouchableOpacity } from 'react-native';
import Constants from 'expo-constants';
import MyContext from './MyContext';
export default function MyComponent() {
const myContext = React.useContext(MyContext);
return (
<TouchableOpacity
onPress={() => {
if (myContext.value) {
alert('true');
myContext.setValue(false);
//changes myContext.value to false
} else {
alert('false');
myContext.setValue(true);
//changes myContext.value to true
}
}}>
<Text>{myContext.value ? 'working' : 'not working'}</Text>
</TouchableOpacity>
);
}
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
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.
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}