How to conditionally use React Native elements - reactjs

I need to use different wrappers to render the screen based on a certain condition.
Example, I have a situation where I need to wrap the content in a view
<View>
<Text> </Text>
<View> </View>
{ and other elements here }
</View>
But in a different scenario, I need the View to be Content from nativeBase.
if useContent variable is true, then render everything using Content as a wrapper, else use View.
How would I best do that ?

Use a ternary operator to help you conditionally render.
render() {
const useContent = true;
return useContent ? (
<Content>
<Text></Text>
<View></View>
</Content>
) : (
<View>
<Text></Text>
<View></View>
</View>
)
}
It may also be better to extract these two pieces into their own components. Just so your component file doesn't become too large and maintainability starts to be a concern. Edit: If you want to keep the children of the component the same and just the wrapping element to change create a second component that renders children:
class WrappingElement extends Component {
render() {
const { children, useContent } = this.props;
return useContent ? (
<Content>{children}</Content>
) : (
<View>{children}</View>
)
}
}
class Foo extends Component {
render() {
<WrappingElement useContent={false}>
<Text>Hello World!</Text>
<View></View>
</WrappingElement>
}
}

<View>
booleanCondition1() && <Text> </Text>
booleanCondition2() && <View> </View>
booleanConditionN() && { and other elements here }
</View>

<View>
{condition == true ?
(
<Text> </Text>
<View> </View>
)
:
(
{ and other elements here }
)
}
</View>

Related

React Native: How does usage of nested components affect performance?

Let's say I have a FlatList component that renders items whose counts can be anywhere between 10 to 100+.
The official doc on FlatList optimization says "Try to avoid a lot of logic and nesting in your list items." I want to understand better what this actually means.
For the item components, I have a couple of options (Examples are simplified, the actual components are much more complicated obviously):
Option 1: Using a nested structure, e.g. :
const ItemComponent = (props) => {
const ChildComponent1 = (props) => {
return (
<View>
<Image source={props.image}/>
</View>
)
}
const ChildComponent2 = () => {
return (
<View>
<Text>This is Child Component.</Text>
</View>
)
}
return (
<View>
<Text>This is {props.name} </Text>
<ChildComponent1 image={props.image}/>
<ChildComponent2/>
</View>
)
}
Option 2: Using a non-nested structure, e.g. :
const ItemComponent = (props) => {
return (
<View>
<Text>This is {props.name} </Text>
<View>
<Image source={props.image}/>
</View>
<View>
<Text>This is Child Component.</Text>
</View>
</View>
)
}
Am I actually better off in terms of performance if I use option 2?
If so, how much performance gain will I have and why? I'd like to understand deeper, but cannot find a resources that explains the rationale in detail.
Thanks!

React Native: Pressable items within a ScrollView causing automatic snap to the top

Hi I'm working on a mobile application in react native. Its a simple app that lets you search for ingredients in a search bar.
I have a small bug which I can't seem to fix regarding the scrollview and setstate. I have a function that displays the search list under my search bar. It is an absolutely positioned element. The bug occurs when I try to scroll down on the scrollview and press on one of the items, instead of the scrollview remaining in place it scrolls directly to the top. Ideally I would like the scrollview to remain in place once the user has pressed on a item. I feel like it has something to do with how I'm calling setstate. The function is as follows:
const [userIngredients, setUserIngredients] = useState([]);
const OutputSearchList = () => {
const clickIngredient = (i) => {
if (!userIngredients.includes(i)) {
setUserIngredients((temp) => [...temp, i]);
} else {
const temp = userIngredients.filter((item) => item !== i);
setUserIngredients(temp);
}
};
return ingredients.map((item) => {
return (
<TouchableOpacity
style={styles.searchBarListItem}
onPress={() => clickIngredient(item)}
key={item}
>
<View style={styles.searchBarListContentChecked}>
<Text style={styles.searchBarListText}>{item}</Text>
<Ionicons
style={styles.searchBarListAdd}
name="checkmark-circle-outline"
size={28}
color="#000000"
/>
</View>
</TouchableOpacity>
);
});
};
and the function is called like so:
<ScrollView>
{ingredients?.length > 0 ? (
<View>
<OutputSearchList />
</View>
) : searchText?.length > 0 ? (
<View>
<Text>No ingredients found</Text>
</View>
) : (
<View>
<Text>Search for an ingredient (e.g. Eggs, Beef, Rice)</Text>
</View>
)}
</ScrollView>

return Button or Custom component from method definition in react native

state = {
isGetOnceButtonClicked: false
}
doAddItems(){
this.state.isGetOnceButtonClicked= true;
}
buttonToggle(){
if(!this.state.isGetOnceButtonClicked){
return(
<Button
type = "clear"
title="Get Once"
onPress={() => this.doAddItems()}
/>
)
}else{
return(
<InputNumberSpinner></InputNumberSpinner>
)
}
}
render() {
return(
<ScrollView>
<View >
{ this.buttonToggle() }
</View>
</ScrollView>
}
I'm new to react-native. I want to show the component dynamically. So created the method in which return the button if condition satisfies else return created custom component but unfortunately it is showing error like "Text strings must be rendered within a component" If we return strings like "something" then it works fine but it throwing error whenever we are returning other than Text.
<ScrollView>
<View >
{ this.state.isGetOnceButtonClicked ? <InputNumberSpinner /> : <Button
type = "clear"
title="Get Once"
onPress={() => this.doAddItems()}
/> }
</View>
</ScrollView>
use a ternary operator for these kinds of tasks, if the value is true then it will show the spinner and if not, it will show the button
make sure to have a function that toggles the state of the button so buttonToggle will be like this:
buttonToggle(){
this.setState({isGetOnceButtonClicked: !this.state.isGetOnceButtonClicked});
}

How can I pass this state?

How can I pass this state to my parent component?
I've tried using callbacks but since the output is in a Text field it doesn't seem to work. onChangeText doesn't recognize when text is changed in a text field? I've also tried using the callback on the Textinput but that doesn't seem to work either. Maybe I'm setting state in the wrong place.
Is this possible?
Child.js
getDLScore(e) {
let i;
i = deadliftScore.scoreSheet[e];
if (e != '') {
if (this.props.mosLevel === '1') {
if (e <= 180) {
return 'fail';
} else {
if (this.state.dlPoints != i) {
this.setState({ dlPoints: i });
}
return i;
}
}
}
}
render() {
return (
<View>
<View style={styles.eventContainer}>
<View styles={styles.child2}>
<Deadlift2
textChange={dlScoreInput => this.setState({ dlScoreInput })}
/>
</View>
<View styles={styles.child3}>
<Text style={styles.titleName}>Points</Text>
<Text style={styles.output}>
{this.getDLScore(this.state.dlScoreInput)}
// I want to pass this to my parent component
</Text>
</View>
</View>
</View>
);
}
const Deadlift2 = props => {
return (
<View>
<TextInput
style={styles.input}
onChangeText={dlScoreInput => props.textChange(dlScoreInput)}
value={props.dlScoreInput}
onKeyPress={props.getDLScore}
/>
</View>
);
};
Parent.js
render() {
return (
<View style={styles.screen2}>
<Text>Points - {this.state.dlScoreInput}</Text>
<View>
First issue is that you forgot to pass down the prop dlScoreInput into <Deadlift2/>. The value is props.dlScoreInput, so add that
<Deadlift2
textChange={dlScoreInput => this.setState({ dlScoreInput })}
dlScoreInput={this.state.dlScoreInput}
/>
The other is that you're not propogating the change up to the parent. You can apply the exact same logic you have that passes the information back up from <Deadlift2 /> to <Child />.
But at that point, you're passing data 2 levels up. After a certain point, you're better off using some type of global state management system (like redux). But if you're adamant about using local state every step of the way, I can show you how to write it.

React native re-render causes view to scroll to top - Do my keys change between renders?

I am using react native, redux and flatlist. I render 10 items and then fetch / render the next 10 items on click (load more). My problem is that when I use setState(to increase the page counter) (thats why I dont use setstate in my code) to fetch more items (i use pagination) or ask whether or not Im fetching (true when fetching -> shown loading sign, false when fetched -> showing items), that triggers
a re-render and my view gets scrolled to top.
Ive been reading for a while now and event.preventDefault (as sometimes suggested) doesnt work and I do believe that my problem is the key / parent tree of the corresponding element changes between renders. I do give each item the key of its ID which is always unique. Am I doing it wrong? Or what else could be causing this? Thanks!
class App extends React.Component {
state = {
page : 0
}
componentDidMount() {
this.props.fetchData(0)
this.state.page++;
}
load = () => {
this.props.fetchData(this.state.page);
this.state.page++;
}
renderRow = ({item}) => {
return (
<Item
key={item.id}
coin={item}
/>
)
}
renderButton() {
return <Button
title="Load more"
onPress={this.load}
/>
}
render() {
if (this.props.isFetching) {
return (
<View>
<Text>Loading</Text>
</View>
)
}
return (
<View style={styles.container}>
<FlatList
style={{width: '100%'}}
data={this.props.data}
renderItem={this.renderRow.bind(this)}
ListFooterComponent={this.renderButton.bind(this)}
/>
</View>
);
}
}
mapStateToProps = state => {
return {
isFetching: state.data.isFetching,
data: state.data.data
}
}
export default connect(mapStateToProps, {fetchData})(App)
Hey change your render method return like below :
return (
{ this.state.isFetching && <View> <Text>Loading</Text> </View>}
<View style={styles.container}>
<FlatList
style={{width: '100%'}}
data={this.props.data}
renderItem={this.renderRow.bind(this)}
ListFooterComponent={this.renderButton.bind(this)}
/>
</View>
);

Resources