Can't make conditional rendering - javascript

I have Header component which I would like to use in multiple screens with multiple use cases such as in MainScreen I want to show only profile icon whereas in other screens I would like to use both backButton and profile icon.
I get isProfileIconVisible and isBackButtonIconVisible from props in Header Component.
this.state = {
isProfileIconVisible: props.isProfileIconVisible,
isBackButtonIconVisible: props.isBackButtonIconVisible
}
I have rendering functions.
_renderProfileIcon () {
let profileIcon = (
<View style={styles.profileButtonContainer} >
<CustomIconButton
onPress={this.props.onProfilePress}
></CustomIconButton>
</View>
);
return profileIcon;
};
_renderBackButtonIcon () {
let backButonIcon = (
<View style={styles.backButtonContainer} >
<CustomIconButton
onPress={this.props.onBackPress}
iconName={"arrow-left"}
></CustomIconButton>
</View>
);
return backButonIcon;
};
and in main render function I am making conditional rendering:
render() {
const { style, isBackButtonIconVisible, isProfileIconVisible, ...otherProps } = this.props;
return (
<View style={styles.container}>
{isBackButtonIconVisible ? this._renderBackButtonIcon : null}
<View style={styles.textContainer} >
<Text style={styles.text}>{this.props.text}</Text>
</View>
{isProfileIconVisible ? this._renderProfileIcon : null}
</View>
)
}
with this setup, I am not able to render either ProfileIcon nor BackButtonIcon.
I got the text prop but not icons.
Header Component propTypes and defaultProps:
Header.propTypes = {
onBackPress: PropTypes.func,
onProfilePress: PropTypes.func,
text: PropTypes.string,
backButtonIconName: PropTypes.string,
isProfileIconVisible: PropTypes.bool,
isBackButtonIconVisible: PropTypes.bool,
};
Header.defaultProps = {
backButtonIconName: 'keyboard-backspace',
isProfileIconVisible: true,
isBackButtonIconVisible: true,
}
And this is how I call Header component from another component:
<Header
text={"Welcome!"}
isProfileIconVisible={true}
isBackButtonIconVisible={false}
onProfilePress={this.handleProfileButtonPress}
style={styles.headerContainer}
/>
Can you help me where I am doing wrong?
Thank you.

Your _renderBackButtonIcon and _renderProfileIcon are functions, you need to call them to get their return values:
render() {
const { style, isBackButtonIconVisible, isProfileIconVisible, ...otherProps } = this.props;
return (
<View style={styles.container}>
{isBackButtonIconVisible ? this._renderBackButtonIcon() : null}
<View style={styles.textContainer} >
<Text style={styles.text}>{this.props.text}</Text>
</View>
{isProfileIconVisible ? this._renderProfileIcon() : null}
</View>
)
}
Note the () after this._renderBackButtonIcon and this._renderProfileIcon.
Side note: There's no reason to have ...otherProps here:
const { style, isBackButtonIconVisible, isProfileIconVisible, ...otherProps } = this.props;
You never use it.
There is an argument for adding text to that list and using it, rather than this.props.text within the return value:
render() {
const { style, isBackButtonIconVisible, isProfileIconVisible, text } = this.props;
return (
<View style={styles.container}>
{isBackButtonIconVisible ? this._renderBackButtonIcon() : null}
<View style={styles.textContainer} >
<Text style={styles.text}>{text}</Text>
</View>
{isProfileIconVisible ? this._renderProfileIcon() : null}
</View>
)
}

Related

React native, undefined is not an object And handle onChange

I made 2 screens home and editing screen. I want to change values from edit screen without redux and context but I don't know how? and also when I click save in editscreen it's throwing error that undefined is not an object (evaluating '_this.props.navigation.goBack') and displaing blank home screencwhy that's happening. Can some one help me please, below is my code
home.js
class Home extends Component {
state = {
modal: false,
editMode: false.
post: [
{
key: "1",
title: "A Good Boi",
des: "He's a good boi and every one know it.",
image: require("../assets/dog.jpg"),
},
{
key: "2",
title: "John Cena",
des: "As you can see, You can't see me!",
image: require("../assets/cena.jpg"),
},
],
};
addPost = (posts) => {
posts.key = Math.random().toString();
this.setState((prevState) => {
return {
post: [...prevState.post, posts],
modal: false,
};
});
};
onEdit = (data) => {
this.setState({ post: { title: data }, editMode: false });
};
render() {
if (this.state.editMode)
return <EditScreen item={item} onEdit={this.onEdit} />;
return (
<Screen style={styles.screen}>
<Modal visible={this.state.modal} animationType="slide">
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<View style={styles.modalContainer}>
<AddPost addPost={this.addPost} />
</View>
</TouchableWithoutFeedback>
</Modal>
<FlatList
data={this.state.post}
renderItem={({ item }) => (
<>
<TouchableOpacity
activeOpacity={0.7}
onPress={() => this.setState({ editMode: true })}
style={styles.Edit}
>
<MaterialCommunityIcons
name="playlist-edit"
color="green"
size={35}
/>
</TouchableOpacity>
<Card>
<Image style={styles.image} source={item.image} />
<View style={styles.detailContainer}>
<Text style={styles.title} numberOfLines={1}>
{item.title}
</Text>
<Text style={styles.subTitle} numberOfLines={2}>
{item.des}
</Text>
</View>
</Card>
</>
)}
/>
</Screen>
Edit.js
import React, { Component } from "react";
import { View, StyleSheet, Image, KeyboardAvoidingView } from "react-native";
import colors from "../config/colors";
import AppButton from "../components/AppButton";
import AppTextInput from "../components/AppTextInput";
class EditScreen extends Component {
render() {
const { item, onEdit, onClose } = this.props;
return (
<KeyboardAvoidingView
behavior="position"
keyboardVerticalOffset={Platform.OS === "ios" ? 0 : 100}
>
<Image style={styles.image} source={item.image} />
<View style={styles.detailContainer}>
<AppTextInput value={item.title} />
<AppTextInput value={item.des} />
</View>
<AppButton
text="Save"
onPress={() => {
onEdit(this.state);
}}
/>
</KeyboardAvoidingView>
);
}
}
export default EditScreen;
AppTextInput.js
function AppTextInput({ icon, width = "100%", ...otherProps }) {
return (
<View style={[styles.container, { width }]}>
<TextInput
placeholderTextColor={defaultStyles.colors.medium}
style={defaultStyles.text}
{...otherProps}
/>
</View>
);
}
Try this:
Edit.js
import React, { Component } from 'react';
import { View, StyleSheet, Image, KeyboardAvoidingView } from 'react-native';
import colors from '../config/colors';
import AppButton from '../components/AppButton';
import AppTextInput from '../components/AppTextInput';
class EditScreen extends Component {
constructor(props) {
super(props);
this.state = { ...props.item };
}
render() {
const { onEdit, onClose } = this.props;
const { title, des, image } = this.state;
return (
<KeyboardAvoidingView
behavior="position"
keyboardVerticalOffset={Platform.OS === 'ios' ? 0 : 100}>
<Image style={styles.image} source={image} />
<View style={styles.detailContainer}>
<AppTextInput
value={title}
onChangeText={text => this.setState({ title: text })}
/>
<AppTextInput
value={des}
onChangeText={text => this.setState({ des: text })}
/>
</View>
<AppButton text="Save" onPress={() => onEdit(this.state)} />
</KeyboardAvoidingView>
);
}
}
export default EditScreen;
onEdit
onEdit = data => {
const newPosts = this.state.post.map(item => {
if(item.key === data.key) return data;
else return item;
})
this.setState({ post: newPosts, editMode: false });
};
Only the direct children of a navigator can access
this.props.navigation
If you want to access that inside the edit screen you can pass it from the Home screen as a prop. Like so:
return <EditScreen item={item} onEdit={this.onEdit} navigation={this.props.navigation} />;
But i don't think you need to go back because you are still on the Home page and just rendering the EditScreen within it. So just changing the state to have editMode: false should be enough

How do I access children components of a reference in React Native Web?

I have some references in a React Native Web application - these references work on React Native, but not RNW.
For example, I have this code:
this.highlight.current._children[i].setNativeProps({ style: { backgroundColor: "black" } });
this.highlight.current._children[i]._children[0]._children[0].setNativeProps({ style: { color: "white" } })
this.highlight.current._children[i]._children[1]._children[0].setNativeProps({ style: { color: "white" } })
Which is based on this:
this.highlight = React.createRef();
Which is passed into a child component as a prop and used as such:
<View ref={this.props.highlight}>
It has several children (who have nested children as well).
However, on the web, there is no ._children at all.
How do I access children?
It's possible to do DOM manipulation directly if Platform.OS === 'web':
let dom = ReactDOM.findDOMNode(this.highlight.current);
... DOM manipulations
But this feels messy and code-duplicating if not absolutely necessary. I'd much rather apply my modifications to the reference directly via the React Native API.
EDIT: More code - here's my structure and a few relevant functions to the problem. I cut out irrelevant parts to try to make the code I posted smaller
class DetailBody extends Component {
constructor() {
super();
}
render() {
return (
<ScrollView >
<Text>{this.props.article.intro}</Text>
<View ref={this.props.highlight}>
{this.props.article.json.results.map((content, index) => (
<View key={index} style={{}}>
{content.pinyin ? (
<Fragment>
<View>
<Text>
{content.pinyin}
</Text>
</View>
<View>
<Text>
{content.simplified}
</Text>
</View>
</Fragment>
) : (
<Fragment>
<View>
<Text>
</Text>
</View>
<View>
<Text>
{content.characters}
</Text>
</View>
</Fragment>
)
}
</View>
))}
</View>
</ScrollView>
)
}
}
class Detail extends Component {
constructor() {
super();
this.state = {
currentVal: 0,
};
this.traverseCharacters = this.traverseCharacters.bind(this)
this.highlight = React.createRef();
}
async traverseCharacters(i) {
this.highlight.current._children[i].setNativeProps({ style: { backgroundColor: "black" } });
this.highlight.current._children[i]._children[0]._children[0].setNativeProps({ style: { color: "white" } })
this.highlight.current._children[i]._children[1]._children[0].setNativeProps({ style: { color: "white" } })
if (i > 0) {
this.clearCharacters(i)
}
}
render() {
return (
<DetailBody {...this.props} article={this.state.article} highlight={this.highlight} />
);
}
}
[Edit]: Since this 'someone's work' is for class component, here is one of my answer using dynamic refs with a functional component : Dynamic refs with functional component. It uses a useRef() hooks to store your dynamic refs, and so they're accessible wherever you want, with a specific id.
After trying things for a moment now, I cannot find a clean way of doing what you want to do. However, there is solutions for this, as you said with the ReactDOM. Another thing that came in my mind would be to set your refs in your child and then pass it to the parent.
Here is someone doing 'dynamic' ref in a .map using the key attribute, maybe it can be of use to you : Someone's work
Now, using direct manipulation isn't a good practice, so using this isntead of ReactDOM.findDOMNode... I don't really know which is one is worse but both are messy i guess.
setNativeProps isn't available on the children element. You either need to provide a refs to the intended child elements yourself before calling setNativeProps on them
For a ref solution you could make use of ref callbacks like below and add a ref to each and every element that you wish to update dynamically
class DetailBody extends Component {
setRef = (i, j, ref) => {
if(this.highlight[i]) {
this.highlight[i][j] = ref;
} else {
this.highlight[i] = {[j]: ref};
}
}
render() {
return (
<ScrollView >
<Text>{this.props.article.intro}</Text>
<View>
{this.props.article.json.results.map((content, index) => (
<View key={index} style={{}} ref= {(ref) => this.setRef(index, 'root', ref)}>
{content.pinyin ? (
<Fragment>
<View ref= {(ref) => this.setRef(index, 0, ref)}>
<Text>
{content.pinyin}
</Text>
</View>
<View ref= {(ref) => this.setRef(index, 1, ref)}>
<Text>
{content.simplified}
</Text>
</View>
</Fragment>
) : (
<Fragment>
<View ref= {(ref) => this.setRef(index, 0, ref)}>
<Text>
</Text>
</View>
<View ref= {(ref) => this.setRef(index, 1, ref)}>
<Text>
{content.characters}
</Text>
</View>
</Fragment>
)
}
</View>
))}
</View>
</ScrollView>
)
}
}
class Detail extends Component {
constructor() {
super();
this.state = {
currentVal: 0,
};
this.traverseCharacters = this.traverseCharacters.bind(this)
this.highlight = {};
}
async traverseCharacters(i) {
this.highlight[i].root.setNativeProps({ style: { backgroundColor: "black" } });
this.highlight[i][0].setNativeProps({ style: { color: "white" } })
this.highlight[i][1].setNativeProps({ style: { color: "white" } })
}
render() {
return (
<DetailBody {...this.props} article={this.state.article} highlight={this.highlight} />
);
}
}

React navigation withNavigation HOC not working in carousel

I'm making a react-native app using expo I'm using the snap-in-carousel library
I want when someone click on the carousel it navigate here is the code
import React, { Component } from 'react';
import { withNavigation } from 'react-navigation';
export default class SliderEntry extends Component {
static propTypes = {
data: PropTypes.object.isRequired,
even: PropTypes.bool,
parallax: PropTypes.bool,
parallaxProps: PropTypes.object
};
get image () {
const { data: { illustration }, parallax, parallaxProps, even } = this.props;
return parallax ? (
<ParallaxImage
source={{ uri: illustration }}
containerStyle={[styles.imageContainer, even ? styles.imageContainerEven : {}]}
style={styles.image}
parallaxFactor={0.35}
showSpinner={true}
spinnerColor={even ? 'rgba(255, 255, 255, 0.4)' : 'rgba(0, 0, 0, 0.25)'}
{...parallaxProps}
/>
) : (
<Image
source={{ uri: illustration }}
style={styles.image}
/>
);
}
render () {
const { data: { title, subtitle}, even, navigation } = this.props;
const uppercaseTitle = title ? (
<Text
style={[styles.title, even ? styles.titleEven : {}]}
numberOfLines={2}
>
{ title.toUpperCase() }
</Text>
) : false;
return (
<TouchableOpacity
activeOpacity={1}
style={styles.slideInnerContainer}
onPress={() => navigation.push('ProfileScreen', {category: title })}
>
<View style={styles.shadow} />
<View style={[styles.imageContainer, even ? styles.imageContainerEven : {}]}>
{ this.image }
<View style={[styles.radiusMask, even ? styles.radiusMaskEven : {}]} />
</View>
<View style={[styles.textContainer, even ? styles.textContainerEven : {}]}>
{ uppercaseTitle }
<Text
style={[styles.subtitle, even ? styles.subtitleEven : {}]}
numberOfLines={2}
>
{ subtitle }
</Text>
</View>
</TouchableOpacity>
);
}
}
i get undefined is not an object (evaluating 'navigation.push')
here is a link to the project on Github: https://github.com/Ov3rControl/Weddi
You're not actually using withNavigation, you're just importing it. You need to pass your component class into the withNavigation HOC.
The way withNavigation works is, you pass in your component, and withNavigation adds the navigation object as a prop to your component.
You're not doing that, hence why this.props.navigation is undefined.
See your modified code below, the export default expression has moved to the bottom, being passed withNavigation(SliderEntry).
Read the manual. https://reactnavigation.org/docs/en/with-navigation.html
import React, { Component } from 'react';
import { withNavigation } from 'react-navigation';
class SliderEntry extends Component {
static propTypes = {
data: PropTypes.object.isRequired,
even: PropTypes.bool,
parallax: PropTypes.bool,
parallaxProps: PropTypes.object
};
get image () {
const { data: { illustration }, parallax, parallaxProps, even } = this.props;
return parallax ? (
<ParallaxImage
source={{ uri: illustration }}
containerStyle={[styles.imageContainer, even ? styles.imageContainerEven : {}]}
style={styles.image}
parallaxFactor={0.35}
showSpinner={true}
spinnerColor={even ? 'rgba(255, 255, 255, 0.4)' : 'rgba(0, 0, 0, 0.25)'}
{...parallaxProps}
/>
) : (
<Image
source={{ uri: illustration }}
style={styles.image}
/>
);
}
render () {
const { data: { title, subtitle}, even, navigation } = this.props;
const uppercaseTitle = title ? (
<Text
style={[styles.title, even ? styles.titleEven : {}]}
numberOfLines={2}
>
{ title.toUpperCase() }
</Text>
) : false;
return (
<TouchableOpacity
activeOpacity={1}
style={styles.slideInnerContainer}
onPress={() => navigation.push('ProfileScreen', {category: title })}
>
<View style={styles.shadow} />
<View style={[styles.imageContainer, even ? styles.imageContainerEven : {}]}>
{ this.image }
<View style={[styles.radiusMask, even ? styles.radiusMaskEven : {}]} />
</View>
<View style={[styles.textContainer, even ? styles.textContainerEven : {}]}>
{ uppercaseTitle }
<Text
style={[styles.subtitle, even ? styles.subtitleEven : {}]}
numberOfLines={2}
>
{ subtitle }
</Text>
</View>
</TouchableOpacity>
);
}
}
// See the component is being wrapped with withNavigation.
export default withNavigation(SliderEntry);

onPress change fragment view

I am using react-native and i have a NavigationDrawer.When i press ListItem i change the page. What i want to do now is to change only half of page's view depending on what user presses.
export default class Some extends Component {
render() {
return (
<View style={styles.View1}>
<View style={styles.hed}>
<View style={styles.imageview}>
<Image style={styles.img} source={require('../images/logo3.png')} />
</View>
</View>
<View style={styles.View2}>
<Page2/>
</View>
</View>
);
}
}
So what i want is depending onPress change to Page 3, Page4.. BUT the first view to stay constant.
You can switch the pages with a logic like this:
export default class Some extends Component {
constructor() {
// You define the first page seen
this.state = { currentPage: 1 };
}
onPress = () => {
// Here your logic to choose the page with:
this.setState({ currentPage: ... });
}
render() {
// All of your pages
const pages = {
1: <Page1 />,
2: <Page2 />,
3: <Page3 />,
};
const { currentPage } = this.state;
return (
<View style={styles.View1}>
<View style={styles.hed}>
<View style={styles.imageview}>
<Image style={styles.img} source={require('../images/logo3.png')} />
</View>
</View>
<View style={styles.View2}>
{/* Display the selected page */}
{pages[currentPage]}
</View>
</View>
);
}
}

ComponentWillmount Fetch returning Null?

My app is always loading, When i drop a debugger around and add a watch onto variable list & res , i get not available.
I'm not exactly sure what's the problem or am i even debugging it correctly?
please advice. I'm trying to achieve Loaded by loading the json into the list.
Update:
I just did a console log and saw data
console.log(this.state.list);
var facemashTab = React.createClass({
getInitialState: function() {
return {
list: [],
currentIndex: 0
};
},
componentWillMount: function() {
fetch('https://randomuser.me/api/?results=5')
.then(res => res.json())
.then(res => this.setState({ list: res }));
},
render: function() {
var contents;
if (!this.state.list.length) {
contents = (
<View style={ styles.loading }>
<Text style={ styles.loadingText }>Loading</Text>
<ActivityIndicatorIOS />
</View>
)
} else {
contents = (
<View style={ styles.content }>
<Text>Loaded</Text>
</View>
)
}
return (
<View style={ styles.container }>
<View style={ styles.header }>
<Text style={ styles.headerText }>XXX</Text>
</View>
<View style={ styles.content }>
{ contents }
</View>
</View>
);
}
});
Not sure if this is the problem, but from my understanding setState inside componentWillMount will not trigger a render phase ? could you try componentDidMount

Categories