React Native last child selector not working - react-native

I'm trying to use the last child selector remove the bottom border from the last Text tag here.
I've used both the EStyleSheet and the StyleSheet but it doesn't seem to be working – the last Text tag still has a bottom border.
I've wrapped the Text tags in View and applied the 'opt' style to the View instead and that also doesn't work.
What am I doing something wrong?
import React from 'react';
import EStyleSheet from 'react-native-extended-stylesheet';
import {
Text,
View,
Image,
} from 'react-native';
EStyleSheet.build()
const styles = EStyleSheet.create({
container:{
flex:1,
},
options:{
},
opt:{
padding:5,
fontSize:25,
borderWidth:1,
borderColor:'black',
},
'opt:last-child': {
borderBottomWidth:0,
}
});
const Settings = () => {
return <View style={styles.container}>
<View style={styles.options}>
<Text style={styles.opt}>Edit profile</Text>
<Text style={styles.opt}>Preferences</Text>
<Text style={styles.opt}>Account settings</Text>
</View>
</View>
};
export default Settings;

To accomplish an item-order aware styling, try something that makes use of EstyleSheet.child like this way:
const items = ['Edit profile', 'Preferences', 'Account settings'];
const Settings = () => (
<View style={styles.container}>
<View style={styles.options}>
{items.map((text, i) => {
const style = EStyleSheet.child(styles, 'opt', i, items.length);
return <Text style={style}>{text}</Text>;
})}
</View>
</View>
);

Related

Get the childrens of an element in react native using useRef

I am trying to understand how to use useRef in React Native and get the children of a View element, but I couldn't figure out to achieve it.
I am trying to use the .focus method in the TextInput component on press on the TouchableOpacity
Declaration:
const input = useRef(null);
TextInputComponent:
<View ref={input}>
<Tex>Email</Text>
<TextInput placeholder=""/>
</View>
Element:
<TouchableOpacity onPress={() => {}}>
<TextInputComponent />
</TouchableOpacity>
i tried input.current.children, but it returns undefined.
This is a working example of how to achieve what you want using on a basic expo init project on TypeScript:
App.tsx
import { StatusBar } from "expo-status-bar";
import { StyleSheet, Text, View, TextInput, TouchableOpacity} from "react-native";
import { useRef, forwardRef} from "react";
const TextInputComponent = forwardRef<TextInput>((props, ref) => {
return (
<View>
<Text>Email</Text>
<TextInput ref={ref} placeholder="Some placeholder" />
</View>
);
});
export default function App() {
const input = useRef<TextInput>(null)
return (
<View style={styles.container}>
<Text>Open up App.tsx to start working on your app!</Text>
<StatusBar style="auto" />
<TouchableOpacity onPress={() => {
input.current?.focus();
}}>
<TextInputComponent ref={input} />
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
});
In this case it is more practical to use a forwardRef so you don't have to loop through the children of the View component to pass a ref from the component (App in this case) that is using TextInputComponent.

Expo / TouchableOpacity onPress() not responding

I'm trying to reproduce a pomodoro app from an online course.
I've been looking at my code for several hours, going back and forth with the course, but can't find my error.
TouchableOpacity or Pressable used in RoundedButton component doesn't trigger onPress()
I tried different command to test if my logic with setStarted was flawe, without success.
Anyone can help me find my bug ?
Here is the link to the expo : https://snack.expo.dev/#battlepoap/focus-time
RoundedButton.js:
import React from 'react';
import { TouchableOpacity, Text, StyleSheet, View } from 'react-native';
export const RoundedButton = ({
style = {},
textStyle = {},
size = 125,
...props
}) => {
return (
<View>
<TouchableOpacity style={[styles(size).radius, style]}>
<Text style={[styles(size).text, textStyle]}>{props.title}</Text>
</TouchableOpacity>
</View>
);
};
const styles = (size) =>
StyleSheet.create({...});
Example with Focus.js where we add a task by pressing on the RoundedButton :
import React, { useState } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { TextInput } from 'react-native-paper';
import { RoundedButton } from '../../components/RoundedButton';
import { fontSizes, spacing } from '../../utils/sizes';
export const Focus = ({ addSubject }) => {
const [tempSubject, setTempSubject] = useState(null);
return (
<View style={styles.container}>
<View style={styles.titleContainer}>
<Text style={styles.title}>What do you want to focus on ?</Text>
<View style={styles.inputContainer}>
<TextInput
style={{ flex: 1, marginRight: spacing.sm }}
onSubmitEditing={({ nativeEvent }) => {
setTempSubject(nativeEvent.text);
}}
/>
<RoundedButton
size={50}
title="+"
onPress={() => addSubject = tempSubject}
/>
</View>
</View>
</View>
);
};
sic In the additional files there was ....
One thing we however need to add in order to make sure the button triggers is the onPress method. This method allows us to trigger the touch event of the user. Don't forget it!!
<TouchableOpacity style={[styles(size).radius, style]}>
<Text
style={[styles(size).text, textStyle]}
onPress={props.onPress}>
{props.title}
</Text>
</TouchableOpacity>

React Native ,Flatlist : how to remove 'unread' note ,once you cliked

I am making a function ,which is at the begining ,all the message are 'unread' .. And once you click the message ,the unread will disappear .like the example as follow:
I had made a component Card.js :
import React from 'react';
import { Image, StyleSheet, Text, TouchableOpacity, View } from 'react-native'
function Card({
img,
userType,
visible,
onPress,
}) {
return (
<TouchableOpacity style={styles.container} onPress={onPress}>
<View style={styles.container}>
<Image style={styles.img} source={img}/>
{/** userType: 0:teacher 1:student 2.other */}
{userType===0&&(
<Text style={styles.txt}>teacher</Text>
)}
{userType===1&&(
<Text style={styles.txt}>student</Text>
)}
{userType===2&&(
<Text style={styles.txt}>other</Text>
)}
{visible===1&&(
<Text style={styles.txt_notice}>Unread</Text>
)}
</View>
</TouchableOpacity>
);
}
const styles = StyleSheet.create({
container : {
width : '90%',
flexDirection : 'row',
justifyContent :'center',
alignItems : 'center',
marginTop : 10,
},
txt : {
fontSize : 18,
marginLeft : 20,
},
img : {
width :100,
height : 100,
borderRadius : 50,
},
txt_notice :{
fontSize : 16,
color :'#DC143C',
marginLeft :10,
}
})
export default Card;
and the screen i used TryScreen.js:
import React, { useState,useEffect } from 'react';
import { FlatList, StyleSheet, View } from 'react-native'
import Card from '../components/Card';
const clients = [
{id:1,img:{uri:'https://i.pinimg.com/564x/64/02/cb/6402cbd3d436caecad2f3e300d67ebd1.jpg'},userType:0,visible:1},
{id:2,img:{uri:'https://i.pinimg.com/236x/34/d6/8c/34d68c7694e1c43204c0dbc623ce4cb8.jpg'},userType:1,visible:1},
{id:3,img:{uri:'https://i.pinimg.com/236x/a0/14/26/a014261951fae6dcc16b4d555f0ef089.jpg'},userType:2,visible:1},
{id:4,img:{uri:'https://i.pinimg.com/236x/96/bf/b6/96bfb641920f527e4fcc9ca23501e222.jpg'},userType:2,visible:1},
{id:5,img:{uri:'https://i.pinimg.com/236x/8b/6e/d9/8b6ed9f0cd6de175537879233a017b0c.jpg'},userType:1,visible:1},
{id:6,img:{uri:'https://i.pinimg.com/236x/0e/16/7f/0e167fab5f152a630fd80dfa1ad3e47c.jpg'},userType:0,visible:1},
]
function TyeScreen(props) {
const[clients_list,setClients]=useState([]);
useEffect(()=>{
setClients(clients);
},[]);
return (
<View style={styles.container}>
<FlatList
data = {clients}
keyExtractor = {item =>item.id.toString()}
renderItem = {({item,index})=>(<Card
img = {item.img}
userType = {item.userType}
visible = {item.visible}
onPress = {()=>{
item.visible -=1;
setClients(item);
}}
/>)}
/>
</View>
);
}
const styles = StyleSheet.create({
container : {
flex : 1,
backgroundColor : '#ffffff',
}
})
export default TyeScreen;
My idea is when click the message ,the 'unread' notice will disappear ... I have tried many ways ,but not worked ... Could you please help me take a look my code ,thank you so much !!
Your clients-list need to be in the state, something like
const [clients, setClients] = useState();
then you initialize them in
useEffect(() => {
setClients(...)
})
and then in onPress you change the property of the selected client and change the state again.
Edit
a simple implementation to force re-rendering
onPress = (client) => {
client.unread = -1;
const newclients = {...clients};
setClients(newclients);
}

How to reuse a UI element in react native

App.js
import React,{ Component } from 'react';
import { StyleSheet,TextInput, View, Button, Text } from 'react-native';
import Toast from 'react-native-simple-toast';
import TextItem from '.src/Components/Textviews/TextViewDisplay';
export default class App extends Component {
state = {
placeName : "",
titleText: "Text view"
}
placeNameChangeHandler = val =>{
this.setState({
placeName : val
})
}
placeSubmitHandler = () =>{
this.setState({
titleText: this.state.placeName.trim()
})
Toast.showWithGravity('This is a long toast at the top.', Toast.LONG, Toast.TOP)
}
render() {
return (
<View style={styles.rootContainer}>
<View style={styles.btnEditContainer}>
<View style ={styles.wrapStyle}>
<TextInput
style = {styles.textInputStyle}
value = {this.state.placeName}
onChangeText = {this.placeNameChangeHandler}
/>
</View>
<View style ={styles.wrapStyle}>
<Button
title="Add"
style ={styles.buttonStyle}
onPress ={this.placeSubmitHandler}/>
</View>
</View>
<View style={styles.textContainer}>
<View style ={styles.wrapStyle}>
<Text
style ={styles.textStyle}>
{this.state.titleText}
</Text>
</View>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
rootContainer: {
height:"100%",
width:"100%",
backgroundColor: "#008000",
flexDirection:"column",
alignItems:"center",
justifyContent: "center"
},
btnEditContainer: {
backgroundColor:"#008080",
flexDirection:"row",
alignItems:"center",
justifyContent: "center"
},
textContainer: {
backgroundColor:"#00FFFF",
flexDirection:"column",
alignItems:"center",
justifyContent: "center"
},
textStyle: {
fontSize: 20,
flexDirection:"column",
alignItems:"center",
justifyContent: "center"
},
buttonStyle: {
//flex:1
},
textInputStyle: {
borderColor:"black",
borderWidth:1,
},
wrapStyle: { marginLeft:5,
marginRight:5 },
});
TextViewDisplay.js
import React from 'react';
import {StyleSheet, View, Text} from 'react-native';
const textItem = (props) => (
<View style={styles.rootElement}>
<Text style={styles.textItem}>
{props.textItem}
</Text>
</View>
);
const styles = StyleSheet.create({
rootElement : {
backgroundColor:"red"
},
textItem : {
color: '#f44336'
}
});
export default textItem;
What I am trying to do:
Instead of Text in App.js file I have written a
TextViewDisplay.js file to reuse.
How to properly implement in render function of App.js
If I undrestood your query correctly, then you just want to replace this
<Text style ={styles.textStyle}>
{this.state.titleText}
</Text>
from your App component with your TextItem (re-usable) component.
As you have already imported your TextItem component, you can do this way
<View style={styles.textContainer}>
<View style ={styles.wrapStyle}>
//This is your re-usable component
<TextItem style = {styles.textStyle}>
{this.state.titleText}
</TextItem >
</View>
</View>
And you just need to change {props.textItem} to {props.children} in your TextItem component.
<View style={styles.rootElement}>
<Text style={{...props.style, ...styles.textItem}}> //Here you can get style from your parent component
{props.children} //This is the child element
</Text>
</View>
Note: Always use PascalCase names for your component.
If you don't want to work with {props.children} and only want to work with {props.textItem}, in that case you need to pass props,
<TextItem style = {styles.textStyle} textItem = {this.state.titleText} />
Now you can use {props.textItem},
<View style={styles.rootElement}>
<Text style={{...props.style, ...styles.textItem}}> //Here you can get style from your parent component
{props.textItem}
</Text>
</View>
Just import your component and instantiate it in any render function:
import textItem from './TextViewDisplay';
render() {
return (
<View style={styles.rootContainer}>
<textItem textItem={'label'} />
...
</View>
}
I would suggest defining the component upper camel case though:
const MyTextItem = (props) => (...)
And giving the property a more meaningful name:
<MyTextItem label={'label'} />
Finally, it's also good to define property types, so other users of your component known which properties to pass, and what type they should be. For example as such:
import PropTypes from 'prop-types';
MyTextItem.propTypes = {
label: PropTypes.string.isRequired,
}
It's very simple re using components in react native :
Step - 1 : Import the function to the screen where you want to reuse that.
ex : import {textItem} from '../pathName'
Step -2 :
render() {
return (
//Create the component of the UI and then use it in render and don't forget to import that:
<TextItem
textItem = 'happy Coding'
/>
)}
Also make sure the component name starts with capital letter.

React Native conditional rendering not working

I want to have a side menu, with a list of categories and when the user selects a category, it should open a scrollable list, right below the category name, with all the pieces of that category.
So I created two list components, one for the categories (SideMenuList) and one for the furniture pieces. I figured I needed to use conditional rendering to render the second list when the user selects the category.
My code:
Side menu code from app.js
state = {
hideMenu: null,
hideList: null
}
sideMenuShow() {
if(!this.state.hideMenu) {
return(
<SideMenu>
<MenuButton onPress = {() => this.setState({hideMenu: true})}/>
<Text style = {{color: 'white', fontSize: 16, fontWeight: 'bold'}}>Furniture</Text>
<SideMenuList onPress = {() => this.setState({hideList: true})}>
{
this.state.hideList ? console.log('yes') : null
}
</SideMenuList>
</SideMenu>
);
}
else {
return(
<SmallSideMenu>
<MenuButton onPress = {() => this.setState({hideMenu: false})}/>
</SmallSideMenu>
);
}
}
SideMenuList.js
import React, { Component } from 'react';
import { View, FlatList, Text, TouchableOpacity } from 'react-native';
import { CardSection } from './common';
import SideMenuItem from './SideMenuItem';
class SideMenuList extends Component {
render() {
return (
<View style = {{flex: 1}}>
<FlatList
style = {{marginBottom: 2}}
data={[
{key: 'Test'},
{key: 'Test2'},
{key: 'Test3'},
{key: 'Test4'},
{key: 'Test5'}
]}
renderItem={({item}) =>
<TouchableOpacity>
<SideMenuItem
onPress = {this.props.onPress}
text={item.key}
>
{this.props.children}
</SideMenuItem>
</TouchableOpacity>}
/>
</View>
);
}
}
export default SideMenuList;
SideMenuItem.js code
import React from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
const SideMenuItem = (props, {onPress}) => {
return (
<View
style = {{flex: 1}}>
<TouchableOpacity
onPress = {onPress}>
<Text style={styles.itemStyle}>{props.text}</Text>
</TouchableOpacity>
{props.children}
</View>
);
}
const styles = {
itemStyle: {
marginTop: 10,
marginRight: 20,
marginLeft: 10,
color: 'white'
}
};
export default SideMenuItem;
My problem right now is that my onPress event is not changing the value of my state property 'hideList', so I can't even check if my solution would actually work. I'm doing a console log when the value is true but it never appears in my console.
Thanks in advance.
You are rendering your SideMenuItem with a TouchableOpacity wrapping it (in your SideMenuList file).
Probably when you are pressing the button, it's triggering SideMenuList button, instead of SideMenuItem button.
Try to remove the TouchableOpacity from SideMenuList and check if it works.
Hope it helps