I have Main component and Bar component. I want to send some info to Bar component.
This is my code:
render() {
<View>
<View>
<Bar />
</View>
<View>
<TouchableOpacity onPress={() => this.props.navigation.navigate('Bar', { Info:'test' }) }>
</TouchableOpacity>
</View>
<View/>
}
I cant send like this because Bar component is in this component. How Can I fix this.
Since your're in the same component ... setState is your answer
class Comp extends React.Component {
state = {
info: '',
};
render() {
const { info } = this.state;
return (
<View>
<View>
<Bar info={info} />
</View>
<View>
<TouchableOpacity
onPress={() => {
this.setState({ info: 'test' });
}}
/>
</View>
</View>
);
}
}
State There are two types of data that control a component: props and
state. props are set by the parent and they are fixed throughout the
lifetime of a component. For data that is going to change, we have to
use state.
Related
I'm learning React Native for the first time. I want to implement a function to show/hide the component by touching the screen, not a specific button.
(Please check the attached file for the example image.)
enter image description here
In this code, I've tried to make a function. if I touch the screen (<View style={style.center}>, then show/hide the renderChatGroup() and renderListMessages() included in <View style={style.footer}>. The source code is below.
In my code, it works. However, the two <View> tag is not parallel. the footer view is center View's child.
I want to make them parallel. but I couldn't find the contents about controlling another <View> tag, not a child. In this code, I used setState, then I couldn't control another the below <View>.
Of course, I tried Fragment tag, but it didn't render anything.
How could I do implement this function? Please help me!
export default class Streamer extends React.Component {
constructor(props) {
super(props);
this.state = {
isVisibleFooter: true,
};
}
renderChatGroup = () => {
const { isVisibleFooter } = this.state;
if (isVisibleFooter) {
return (
<ChatInputGroup
onPressHeart={this.onPressHeart}
onPressSend={this.onPressSend}
onFocus={this.onFocusChatGroup}
onEndEditing={this.onEndEditing}
/>
);
}
return null;
};
onPressVisible = () => {
const { isVisibleFooter } = this.state;
this.setState(() => ({ isVisibleFooter: !isVisibleFooter }));
};
render() {
return (
<SafeAreaView style={styles.container}>
<SafeAreaView style={styles.contentWrapper}>
<View style={styles.header} />
<TouchableWithoutFeedback onPress={this.onPressVisible}>
<View style={styles.center}>
<View style={styles.footer}>
{this.renderChatGroup()}
{this.renderListMessages()}
</View>
</View>
</TouchableWithoutFeedback>
</SafeAreaView>
</SafeAreaView>
);
}
}
Firstly I would highly recommend you use react native with functional components and React Hooks as they alternative will soon will be deprecated.
Since onPress is not available on the View Component, you would need to replace it with TouchableWithoutFeedback as you have already done in your code.
For Showing/Hiding a view you would need to use a conditional operator.
export default class Streamer extends React.Component {
constructor(props) {
super(props);
this.state = {
isVisibleFooter: true,
};
}
renderChatGroup = () => {
const { isVisibleFooter } = this.state;
if (isVisibleFooter) {
return (
<ChatInputGroup
onPressHeart={this.onPressHeart}
onPressSend={this.onPressSend}
onFocus={this.onFocusChatGroup}
onEndEditing={this.onEndEditing}
/>
);
}
return null;
};
onPressVisible = () => {
this.setState(() => ({ isVisibleFooter: !isVisibleFooter }));
const { isVisibleFooter } = this.state;
};
render() {
return (
<SafeAreaView style={styles.container}>
<SafeAreaView style={styles.contentWrapper}>
<View style={styles.header} />
<TouchableWithoutFeedback onPress={this.onPressVisible}>
<View style={styles.center}>
{isVisibleFooter && <View style={styles.footer}>
{this.renderChatGroup()}
{this.renderListMessages()}
</View>}
</View>
</TouchableWithoutFeedback>
</SafeAreaView>
</SafeAreaView>
);
}
}
Here you can see i have replaced
<View style={styles.footer}>
{this.renderChatGroup()}
{this.renderListMessages()}
</View>
with
{isFooterVisible && <View style={styles.footer}>
{this.renderChatGroup()}
{this.renderListMessages()}
</View>}
stating that to only display the Footer View when
const isFooterVisible = true;
This Is My Home Page Code:
import React from "react";
//More Imports
export default class Home extends React.Component {
//Some Code
render() {
const { navigation } = this.props;
return (
<ScrollView>
//Some Code
<View style={styles.barContainer}>
<Button
title="Add Lesson"
onPress={() => navigation.navigate("ThisLesson")}
/>
</View>
//Some Code
{ScrollViewWithCards}
//Some Code
</ScrollView>
);
}
}
const styles = StyleSheet.create({
//Some Style
});
const cards = [
{
day: "3",
month: "Jan",
numberOfPeople: "4",
time: "17:00-18:00",
title: "Dance Class",
image: require("../../../assets/images/image1.jpeg"),
},
//More Cards...
];
const ScrollViewWithCards = (
<ScrollView>
{cards.map((card, index) => (
<View key={index} style={styles.cardContainer}>
<TouchableOpacity
onPress={() =>
navigation.navigate("ThisLesson", {
image: card.image,
day: card.day,
month: card.month,
time: card.time,
title: card.title,
numberOfPeople: card.numberOfPeople,
})
}
>
<HomeCard
image={card.image}
day={card.day}
month={card.month}
time={card.time}
title={card.title}
numberOfPeople={card.numberOfPeople}
/>
</TouchableOpacity>
</View>
))}
</ScrollView>
);
I'm mapping through an array of static data and rendering cards unto the screen
I made the cards pressable so that they take me to another page,
when I click the card it Returns an error:Reference Error: Can't find variable: navigation
But the Button Above the Cards Works Just Fine
What Am I Doing Wrong?
I tried the useNavigation Hook but it didn't work either
Update
This is my HomeCard component:
import React from "react";
//More Imports
const HomeCard = (props) => {
return (
<View style={styles.container}>
//Some Code
</View>
);
};
export default HomeCard;
const styles = StyleSheet.create({
//Some Style
});
const smallAvatars = [
//Some Array
];
I passed {navigation} to ScrollViewWithCards like so:
const ScrollViewWithCards =({navigation})=>()
but now I'm Getting another Error TypeError: undefined is not an object (evaluating 'navigation.navigate')
Solution
The Solution for this Problem is to transform ScrollViewWithCards to a function component, then pass props to it and add return:
const ScrollViewWithCards = (props) => {
return (
<ScrollView>
{cards.map((card, index) => (
<View key={index} style={styles.cardContainer}>
<TouchableOpacity
onPress={() =>
props.navigation.navigate("ThisLesson", {
image: card.image,
day: card.day,
month: card.month,
time: card.time,
title: card.title,
numberOfPeople: card.numberOfPeople,
})
}
>
<HomeCard
image={card.image}
day={card.day}
month={card.month}
time={card.time}
title={card.title}
numberOfPeople={card.numberOfPeople}
/>
</TouchableOpacity>
</View>
))}
</ScrollView>
);
};
and then in the main render:
<ScrollViewWithCards navigation={this.props.navigation} />
You are setting the const navigation inside the render function, and it wont be accessible inside other functions, so you have to use
this.props.navigation.navigate
Then you can simply do
const ScrollViewWithCards =()=> (
<ScrollView>
{cards.map((card, index) => (
<View key={index} style={styles.cardContainer}>
<TouchableOpacity
onPress={() =>
this.props.navigation.navigate("ThisLesson", {
image: card.image,
day: card.day,
month: card.month,
time: card.time,
title: card.title,
numberOfPeople: card.numberOfPeople,
})
}
>
<HomeCard
image={card.image}
day={card.day}
month={card.month}
time={card.time}
title={card.title}
numberOfPeople={card.numberOfPeople}
/>
</TouchableOpacity>
</View>
))}
</ScrollView>
);
In the routing section, you need to mention the both component like this,
<Stack.Screen name="<your component name>" component={your component class} />
please don't forget to import the files at the above.
and then you can use the navigation props like,
this.props.navigation //for class component
props.navigation //for functional component
or if you have parent child relation in your compoent try this one:
<YOUR_COMPONENT navigation={props.navigation}/> // functional component
<YOUR_COMPONENT navigation={this.props.navigation}/> // class component
I have a react-native view like this.
<Parent>
<Header/>
</Parent>
The header is also a view with some text input fields and icons. The Parent is another view created by adding the Header component. So what I want to do is, when I type some text on the text field which is located in the Header view, I want to take that value to the Parent view props. How to do this??? I've tried some answers that showed in StackOverflow. But they didn't give me what I expected.
For someone who wants to see the full code, This is the parent screen.
export default class ParentScreen extends Component {
constructor(props) {
super(props);
this.state = {
objects: null,
indicator: true,
};
}
render(){
<View style={styles.container}>
<HeaderBar />
</View>
}}
And this is the Header screen.
export default class HeaderBar extends Component {
constructor(props) {
super(props);
}
state = {
searchEnabled: false
};
render() {
return (
<View style={styles.navigationBar}>
<View style={styles.titleArea}>
{this.state.searchEnabled === true ? (
<View style={styles.titleArea}>
<Icon
name="arrow-back"
type="Ionicons"
color="black"
onPress={() => this.setState({ searchEnabled: false })}
/>
<TextInput
placeholder="Search"
placeholderTextColor="white"
style={styles.input}
onChangeText={text => this.setState({ filterKey: text })}
/>
</View>
) : (
<View style={styles.titleArea}>
<Image
style={styles.profileImage}
source={require("../../images/user_image_1.jpg")}
/>
</View>
)}
</View>
</View>
);
}
}
Define a function in the parent View, something like:
onChangeText = (text) => {
this.setState({
myUpdatedText: text
})
}
Then, pass it to the child as a prop:
<HeaderBar onChangeText={this.onChangeText} />
So in the child code you can use it like:
<TextInput
placeholder="Search"
placeholderTextColor="white"
style={styles.input}
onChangeText={this.props.onChangeText}
/>
I want, instead of using the default Alert.alert method, to create my own so that I could change the background of my Alert, whether it's successful or not. To do so, I use a Modal, which put in my root component, App.js (so that the modal can appear on each screen) :
App.js
// ...
const Navigation = createAppContainer(MainNavigator);
// Render the app container component with the provider around it
class App extends React.Component {
render() {
return (
<Provider store={store}>
<Navigation />
<CustomAlert /> {/* <--- here */}
</Provider>
);
}
}
I do so because I don't want to place my <CustomAlert /> component on each Screen that I use. If someone has another approach I'll take it.
CustomAlert.js
// ... (imports)
class CustomAlert extends Component {
render() {
const { customStyle } = this.props;
const { title, description, enabled } = this.props;
const { reinit } = this.props; // dispatch
return (
<View>
<Modal
animationType="none"
transparent
visible={enabled}
>
<View style={styles.fullScreen}>
<View style={[styles.content, customStyle]}>
<View style={{ flex: 1 }}>
<View style={styles.title}>
<Text style={{ fontWeight: 'bold' }}>{title}</Text>
</View>
<View style={styles.desc}>
<Text style={{ textAlign: 'center' }}>{`${description}`}</Text>
</View>
<View style={styles.buttons}>
<TouchableOpacity
onPress={() => {
reinit();
}}
style={styles.button}
>
<Text>OK</Text>
</TouchableOpacity>
</View>
</View>
</View>
</View>
</Modal>
</View>
);
}
}
function mapStateToProps(state) {
return {
type: state.alert.type,
title: state.alert.title,
description: state.alert.description,
enabled: state.alert.enabled,
};
}
function mapDispatchToProps(dispatch) {
return {
reinit: () => dispatch(reinitState()),
};
}
export default stylesWrapper(connect(mapStateToProps, mapDispatchToProps)(CustomAlert));
I proceed like that:
In my function that shows the alert, I simply change my redux store:
helper.js
// ...
export const infoAlert = (title, msg, type = 'error') => {
store.dispatch(setTitle(title));
store.dispatch(setDescription(msg));
store.dispatch(setType(type));
store.dispatch(setEnabled(true));
};
// ...
The behaviour is good but I have this warning :
Warning: Cannot update during an existing state transition (such as within render). Render methods should be a pure function of props and state.
I suspects this has one reason between those two:
Using store.dispatch as a general function doesn't know anything about the component that called him, so this message indicates that It might rerender it (which is bad)
infoAlert is called inside a functionnal component (Redux Form), and it's state shouldn't change (something like that)
Here an example of call infoAlert (at the bottom):
const renderField = ({
input,
label,
keyboardType,
secureTextEntry,
autoCapitalize,
autoCorrect,
meta: {
touched, error, warning, active, dirty,
},
}) => (
<View>
<TextInput
style={[styles.textInput, (active) && styles.active,
(touched) && (((error) && styles.error)
|| ((warning) && styles.warning))]}
{...input}
onChangeText={input.onChange}
onBlur={input.onBlur}
onFocus={input.onFocus}
value={input.value}
keyboardType={keyboardType}
placeholder={label}
secureTextEntry={secureTextEntry}
autoCapitalize={autoCapitalize}
autoCorrect={autoCorrect}
/>
{(touched && !active && dirty) && (((error) && infoAlert('Erreur', error))
|| ((warning) && infoAlert('Attention', warning)))}
</View>
);
Do someone has an idea of how to remove this Warning ?
Do someone has another way (maybe simpler) to create a custom alert (without putting the component itself everywhere) ?
Screens
I am doing stack navigation but I can't seem to be able to navigate I will get this error "Can't find variable: navigate" Here is the screenshot of my android emulator
This is my App class(main)
export default class App extends Component {
render() {
return (
<View style={styles.container}>
<Header/>
<AppNavigator/>
</View>
);
}
}
const AppNavigator = StackNavigator({
Cluster1: {
screen: Cluster1,
},
Play: {
screen: Play,
},
});
This is my Cluster1 class
export default class Cluster1 extends Component{
render(){
return(
<View>
<SectionList
renderSectionHeader={({ section }) => {
return (<SectionHeader section={section} />);
}}
sections={ClusterData}
keyExtractor={(item, index) => item.name}
>
</SectionList>
</View>
);
}
}
class SectionHeader extends Component {
render() {
return (
<View style={styles.header}>
<Text style={styles.headertext}>
{this.props.section.title}
</Text>
<TouchableOpacity onPress={() => { navigate("Play");}}>
<Text style ={styles.Play}>Play
</Text>
</TouchableOpacity>
</View>
);
}
}
navigation object only exist in the screen component. (not exist in the nested components). you can pass it into the nested component using props
export default class Cluster1 extends Component{
render(){
return(
<View>
<SectionList
renderSectionHeader={({ section }) => {
return (<SectionHeader navigation={this.props.navigation} section={section} />);
}}
sections={ClusterData}
keyExtractor={(item, index) => item.name}
>
</SectionList>
</View>
);
}
}
class SectionHeader extends Component {
render() {
return (
<View style={styles.header}>
<Text style={styles.headertext}>
{this.props.section.title}
</Text>
<TouchableOpacity onPress={() => { this.props.navigation.navigate("Play");}}>
<Text style ={styles.Play}>Play
</Text>
</TouchableOpacity>
</View>
);
}
}
Include on your SectionHeader the this.props.navigation something like this:
<SectionHeader navigation={this.props.navigation}/>
because the props.navigation are by default on your parent component
and on SectionHeader component you will access to navition like:
..
goToSignUp() {
this.props.navigation.navigate('SignUp');
}
..
For me also was confusing before. Cheers!
You can use this rather than navigate :
this.props.navigation.navigate('Play')
Hope this is helpful.