SearchBar search - reactjs

I'm trying to create a library app and I want to be able to search stories by their title. I created a search bar from the SearchBar component, but I don't know when to call a used defined function that allows me to get the books with the searched word.
Could anyone tell me the prop we need to use? More specifically, how do I know the little magnifying glass on the left is clicked? Thank you!
render() {
var count = 0
var title = "";
var author = "";
return(
<ScrollView>
<View>
<Header
backgroundColor = {"#b7e4c7"}
centerComponent = {{
text:"Available stories",
style: {
color:'white',
fontSize:20,
fontWeight:'bold'
}
}}/>
<SearchBar
lightTheme
containerStyle = {{marginTop:5}}
placeholder="Which book would you like to search"
onChangeText={text =>
{this.setState({search:text})}}
value ={this.state.search}
/>
{this.state.tas.map((element) => {
count = count + 1
if(count%2 == 1) {
title = element;
}
else {
author = element;
return(
<TouchableOpacity style = {styles.storyButton} key = {element}>
<Text style = {styles.buttonText}>Title: {title}</Text>
<Text style = {styles.buttonText}>Author: {author}</Text>
</TouchableOpacity>
)
}
})}
</View>
</ScrollView>
)
}

Please have a look at fetchDetails and onChangeText
import React from "react";
import "./styles.css";
export default class App extends React.Component {
// state init
fetchDetails = () =>{
const search = this.state.search
//DO fetch
}
render() {
var count = 0
var title = "";
var author = "";
return(
<ScrollView>
<View>
<Header
backgroundColor = {"#b7e4c7"}
centerComponent = {{
text:"Available stories",
style: {
color:'white',
fontSize:20,
fontWeight:'bold'
}
}}/>
<SearchBar
lightTheme
containerStyle = {{marginTop:5}}
placeholder="Which book would you like to search"
onChangeText={text =>{
this.setState({search:text})
this.fetchDetails()
}}
value ={this.state.search}
/>
{this.state.tas.map((element) => {
count = count + 1
if(count%2 == 1) {
title = element;
}
else {
author = element;
return(
<TouchableOpacity style = {styles.storyButton} key = {element}>
<Text style = {styles.buttonText}>Title: {title}</Text>
<Text style = {styles.buttonText}>Author: {author}</Text>
</TouchableOpacity>
)
}
})}
</View>
</ScrollView>
)
}
}

Related

How to show see more in React Native Web

How do I do the "see more" in react native web? My problem is on the onLayout and onTextLayout.
How do I determine to show the "see more" if its too long?
const ReadMoreText = () => {
const [textShown, setTextShown] = useState(false) //To show ur remaining Text
const [lengthMore, setLengthMore] = useState(false) //to show the "Read more & Less Line"
const toggleNumberOfLines = () => {
//To toggle the show text or hide it
setTextShown(!textShown)
}
const onTextLayout = useCallback((e) => {
// setLengthMore(e.nativeEvent.lines.length >= 4) //to check the text is more than 4 lines or not
// console.log(e.nativeEvent);
}, [])
return (
<View style={styles.mainContainer}>
<Text
onLayout={onTextLayout}
numberOfLines={textShown ? undefined : 4}
>
SAMPLE TEXT HERE...
</Text>
{lengthMore ? (
<Text
onPress={toggleNumberOfLines}
>
{textShown ? 'Read less...' : 'Read more...'}
</Text>
) : null}
</View>
)
}
I spent a long time looking into this and my solution was to implement a really simple component (below) which just limits the lines and allows you to show more. Maybe this is enough for you.
If not you could create a ref and use measureInWindow to get the measurement of the text container. ref.measureInWindow((x, y, width, height) => {})
import * as React from 'react'
import { Text } from 'react-native'
export default function ReadMore({ children, numLines = 4, style }) {
const [lines, setLines] = React.useState(numLines)
return (
<>
<Text numberOfLines={lines} style={style}>
{children}
</Text>
{lines === 0 ? (
<Text onPress={() => setLines(numLines)} style={{fontWeight:'bold'}}>
Read Less
</Text>
) : (
<Text onPress={() => setLines(0)} style={{fontWeight:'bold'}}>
Read More
</Text>
)}
</>
)
}
You can simply define a line height and then divide that by the total height of the text to get the number of lines. Here's how I did it.
import { useState, useCallback } from 'react'
import { Platform, LayoutChangeEvent } from 'react-native'
const LINE_HEIGHT = 30
const [numOfLines, setNumOfLines] = useState(0)
const [isInitialLayoutLoaded, setInitialLayoutLoaded] = useState(false)
const onLayout = useCallback((e: LayoutChangeEvent) => {
if (!isInitialLayoutLoaded && Platform.OS === 'web') {
setNumOfLines(e.nativeEvent.layout.height / LINE_HEIGHT)
setInitialLayoutLoaded(true)
}
},
[isInitialLayoutLoaded],
)
Then simply use it like:
<Text onLayout={onLayout}>A very long piece of text</Text>
#Joseph
just update your code
import * as React from 'react'
import { Text } from 'react-native'
export default function ReadMore({ children, numLines = 4, style }) {
const [lines, setLines] = React.useState(numLines)
const [showButton, setShowButton] = React.useState(false)
const onTextLayout = (e) => {
if(e.nativeEvent.lines.length > numLines){
setShowButton(true)
}
}
return (
<>
<Text onTextLayout={onTextLayout} numberOfLines={lines} style={style}>
{children}
</Text>
{showButton && <>
{lines === 0 ? (
<Text onPress={() => setLines(numLines)} style={{fontWeight:'bold'}}>
Read Less
</Text>
) : (
<Text onPress={() => setLines(0)} style={{fontWeight:'bold'}}>
Read More
</Text>
)}</>}
</>
)}

Trouble updating flatList on the first screen, when some item from the flatlist is deleted in the second screen

I am having trouble updating flatList on the first screen when some item from the flatlist is deleted or added on the second screen to the flatlist. My flatlist is of bands and when I click on the individual name I see the details like band members, songs, albums, etc. I've two screens currently, the home screen with a list and add or delete screen, where items on the list can be customized. This is what I have so far, but I can't manage to update the list on the main page when something is removed on the second page from the list.
import React, { useState, useEffect } from 'react';
import { StyleSheet, Text, View, FlatList, TouchableOpacity, TouchableHighlight, Button, Alert, Modal, TextInput, ScrollView, SafeAreaView } from 'react-native';
import 'react-native-gesture-handler';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
function UselessTextInput(props) {
return (
<TextInput
{...props}
style={{ height: 30, borderWidth: 1, padding: 6, paddingTop: 10, margin: 5, color: '#FFFCD3', borderColor: '#A5C9FA', }}
editable
maxLength={30}
/>
); t
}
const musiciansArray = [
{ id: 1, key: 'A', value: 'Pink Floyd', members: 4, songs: 165 },
{ id: 2, key: 'B', value: 'Deep Purple', members: 5, songs: 70 },
{ id: 3, key: 'C', value: 'AC/DC', members: 5, songs: 80 },[![enter image description here][1]][1]
];
function HomeScreen({ navigation, route }) {
const [listItems, setListItems] = useState(musiciansArray);
const [store1, setStore1] = useState("");
const [idDeleteInput, setIdDeleteInput] = useState();
//const [exampleArray, setExampleArray] = useState(musiciansArray);
const ItemView = ({ item }) => {
return (
<View>
<Text
style={styles.item}
onPress={() => getItem(item)}>
{item.value}
</Text>
</View>
);
};
const ItemSeparatorView = () => {
return (
<View style={styles.fList} />
);
};
const EmptyListMessage = ({ item }) => {
return (
<Text
style={styles.emptyListStyle}
onPress={() => getItem(item)}>
Empty list found.
</Text>
);
};
const getItem = (item) => {
alert(' id: ' + item.id + '\n' + ' key: ' + item.key + '\n' + ' Band Name: ' + item.value + '\n' + ' Members: ' + item.members + '\n' + ' Songs: ' + item.songs)
};
useEffect(() => {
if (route.params?.post) {
setListItems(JSON.parse(route.params?.post).nameOfBand);
}
}, [route.params?.post]);
return (
<View style={styles.container}>
<View >
<View>
<Text style={styles.text1}>Bands</Text>
<FlatList
data={listItems}
ItemSeparatorComponent={ItemSeparatorView}
renderItem={ItemView}
keyExtractor={item => item.id}
ListEmptyComponent={EmptyListMessage} />
</View>
<View style={styles.fixToText}>
<TouchableHighlight style={styles.button2}
onPress={() => navigation.navigate('Input')}
activeOpacity={0.6}
underlayColor='red'
>
<Text style={styles.textButton}>Add or Delete</Text>
</TouchableHighlight>
</View>
</View>
</View>
);
}
function InputScreen({ navigation,route }) {
const [listItems, setListItems] = useState(musiciansArray);
const [nameOfBand, setNameOfBand] = useState('');
const [keyOfBand, setkeyOfBand] = useState('');
const [memInput2, setMemInput2] = useState('');
const [songInput2, setSongInput2] = useState('');
const [idDeleteInput, setIdDeleteInput] = useState();
const [idDeleteInput2, setIdDeleteInput2] = useState();
const [exampleArray, setExampleArray] = useState(musiciansArray);
const delItem = () => {
navigation.navigate('InputScreen', { post: JSON.stringify({ text: idDeleteInput2 }) });
if (idDeleteInput2 > musiciansArray.length || idDeleteInput2 === "" || isNaN(idDeleteInput2)) {
Alert.alert('No such id is present.');
} else if (exampleArray.length === 0) {
Alert.alert('Nothing can be deleted, empty list found.');
}
else {
Alert.alert("Item with id " + idDeleteInput2 + " sucessfully deleted.");
const filteredArray = exampleArray.filter(item => item.id != idDeleteInput2);
setExampleArray(filteredArray);
}
}
const alertAdd = () => {
navigation.navigate('InputScreen', { post: JSON.stringify({ text: nameOfBand, text: keyOfBand , text: memInput2 ,text: songInput2 }) });
if (nameOfBand == "" || keyOfBand == "" || memInput2 == "" || songInput2 == "") {
Alert.alert("No new item has been added, as atleast a field is empty.");
} else {
var newArray = [...exampleArray, { id: exampleArray.length + 1, key: keyOfBand, value: nameOfBand, members: memInput2, songs: songInput2 }];
setExampleArray(newArray);
Alert.alert("Item " + nameOfBand + " added successfully.");
}
};
return (
<View style={styles.container}>
<View>
<Text style={styles.text3}>Add</Text>
<Text style={styles.text4}>Band Name:</Text>
<UselessTextInput
multiline
numberOfLines={4}
value={nameOfBand}
onChangeText={itemText => setNameOfBand(itemText)}
/>
<Text style={styles.text4}>Key e.g. A, B, C:</Text>
<UselessTextInput
multiline
numberOfLines={4}
value={keyOfBand}
onChangeText={itemText => setkeyOfBand(itemText)}
/>
<Text style={styles.text4}>No. of Members:</Text>
<UselessTextInput
multiline
numberOfLines={4}
value={memInput2}
onChangeText={itemText => setMemInput2(itemText)}
/>
<Text style={styles.text4}>No. of Songs:</Text>
<UselessTextInput
multiline
numberOfLines={4}
value={songInput2}
onChangeText={itemText => setSongInput2(itemText)}
/>
</View>
<View style={styles.fixToText}>
{/* <TouchableOpacity onPress={alertAdd}
style={styles.button2}>
<Text style={styles.textButton}>Press To Add</Text>
</TouchableOpacity> */}
<TouchableOpacity
// onPress={alertAdd}
onPress={alertAdd}
// Pass params back to home screen
style={styles.button2}>
<Text style={styles.textButton}>Press To Add</Text>
</TouchableOpacity>
</View>
<View>
<Text style={styles.text3}>Delete</Text>
<Text style={styles.text4}>Id to delete:</Text>
<UselessTextInput
multiline
numberOfLines={4}
value={idDeleteInput2}
onChangeText={delID => setIdDeleteInput2(delID)} />
</View>
<View style={styles.fixToText2}>
<TouchableOpacity onPress={delItem}
style={styles.button3}>
<Text style={styles.textButton}>Press To Del</Text>
</TouchableOpacity>
</View>
</View>
);
}
const Stack = createStackNavigator();
const App = () => {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Input" component={InputScreen} />
</Stack.Navigator>
</NavigationContainer>
);
};
export default App;
This is what my app looks like right now:
I know this is a lot of code, but I have mentioned the gist of the concept here, I am probably missing something here regarding navigation, as I am pretty new to that concept. Thanks in advance!
The reason for screen not being updated is when you are navigating back to the screen, the screen is not re rendered as it is already rendered. The changes made on the second screen doesn't re renders the previous screen. In case to achieve that, if you are using react navigation then there is a option to add Event Listener for screen focus, you can use that event to update your screen.
See: https://reactnavigation.org/docs/navigation-events/

onChange firing when picker value not selected

I have a login form with conditional statements to display option lists, when a user selects a value from the first picker (Year), the picker below is displayed (Group), when a group is selected the third picker is displayed.
I have set functions to each pickers onChange.
This works fine until I start changing selected values. If I reselect a Year value from the first picker, this.onGroupChange is invoked on the second picker (Group).
Why is this invoked when that picker's value hasn't changed? How can I prevent firing when a previous value is changed?
class HomeScreen extends Component {
static navigationOptions = {
title: ""
};
constructor(props) {
super(props);
this.state = {};
this.onStudentChange = this.onStudentChange.bind(this);
this.onGroupChange = this.onGroupChange.bind(this);
this.onParentChange = this.onParentChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
}
componentDidMount() {}
onParentChange(e) {
const { setYear: year } = this.props.credentials;
this.props.dispatch(setYear(e));
}
onGroupChange(e) {
console.log(this.props);
const { setYear } = this.props.credentials;
const { categories } = this.props.categories;
if (e !== null) {
this.props.dispatch(setGroup(e, categories, setYear));
}
}
onStudentChange(e) {
if (e !== "") {
this.props.dispatch(setStudent(e));
}
}
onSubmit(e) {
this.props.navigation.navigate("Month");
}
render() {
const {
setYear,
setGroup,
setStudent,
showStudent
} = this.props.credentials;
const categories = this.props.categories;
const isLoading = this.props.isLoading;
if (isLoading || !categories) {
return (
<View style={[styles.container, styles.horizontal]}>
<ActivityIndicator size="large" color="#fff" />
</View>
);
}
return (
<View style={styles.container}>
<Image
source={require("../img/logoGreen.png")}
style={{ width: 300, height: 200 }}
/>
<View style={{ backgroundColor: "white" }}>
<Picker
selectedValue={setYear}
label="Year"
onChange={this.onParentChange}
options={categories}
/>
{setYear ? (
<Picker
selectedValue={setGroup}
label="Group"
// invoked when picker above is changed.
onChange={this.onGroupChange}
options={
categories.find(category => {
return category.id == setYear;
}).options
}
/>
) : null}
{setGroup && showStudent ? (
<Picker
selectedValue={setStudent}
label="Student"
onChange={this.onStudentChange}
options={
categories
.find(category => {
return category.id == setYear;
})
.options.find(category => {
return category.id == setGroup;
}).options
}
/>
) : null}
{(setYear && setGroup && !showStudent) || setStudent ? (
<TouchableHighlight
style={styles.button}
onPress={this.onSubmit}
>
<Text style={styles.buttonText}> Submit</Text>
</TouchableHighlight>
) : null}
</View>
</View>
);
}
}

react navigation custom tabBarComponent?

the navigationOptions code like that.
static navigationOptions = ({navigation})=>({
tabBarLabel:'查看',
headerTitle:navigation.state.params.title,
tabBarIcon: ({ tintColor,focused }) => (
<Image style={SKIN.tabImage} source={focused?AppImages.MyPost.lookchoose:AppImages.MyPost.look}/>
),
});
this is my Tab componet,how I can get tabBarLabel and tabBarIcon?
export default class Tab extends Component {
renderItem = (route, index) => {
const {
navigation,
jumpToIndex,
} = this.props;
const focused = index === navigation.state.index;
const color = focused ? this.props.activeTintColor : this.props.inactiveTintColor;
return (
<TouchableOpacity
key={index}
style={styles.tabItem}
onPress={() => jumpToIndex(index)}
>
<View
style={styles.tabItem}>
{this.props.renderIcon(color,focused)}
<Text style={{ color }}>{this.props.getLabel()}</Text>
</View>
</TouchableOpacity>
);
};
render(){
console.log('Tab this.props',this.props);
const {navigation,} = this.props;
const {routes,} = navigation.state;
return (
<View style={styles.tab}>
{routes && routes.map(this.renderItem)}
</View>
);
}
}
I custom Tab,now I want use that but some bug show me.
like that,
imagebug
please help me...
try updating the render method with this code:
render(){
console.log('Tab this.props',this.props);
const {navigation,} = this.props;
const {routes,} = navigation.state;
return (
<View style={styles.tab}>
//pass down the route and the index to the renderItem function
{routes && routes.map((route,index) => this.renderItem(route, index) )}
</View>
);
renderItem = (route, index) => {
const {
navigation,
jumpToIndex,
} = this.props;
const focused = index === navigation.state.index;
const color = focused ? this.props.activeTintColor : this.props.inactiveTintColor;
let TabScene = {
focused:focused,
route:route,
tintColor:color
};
return (
<TouchableOpacity
key={route.key}
style={styles.tabItem}
onPress={() => jumpToIndex(index)}
>
<View
style={styles.tabItem}>
{this.props.renderIcon(TabScene)}
<Text style={{ color }}>{this.props.getLabel(TabScene)}</Text>
</View>
</TouchableOpacity>
);
};

Iterating over children in stateless components react/react-native

I was working on a react-native application and I created a common component for display list items.
<View style={styles.container}>
<ItemsWithSeparator style={styles.itemsContainer}>
<AppRow />
<AppRow />
</ItemsWithSeparator>
</View>
Now, my ItemListSeparator is just iterates over the children and renders the list, so I thought I would make this a stateless component.
const ItemsWithSeparator = function ({children,style}) {
const childrenList = [];
const length = React.Children.count(children);
React.Children.forEach(
children,
(child,ii) => {
childrenList.push(child);
if (ii !== length -1) {
childrenList.push(
<View
key={`separator-${ii}`}
style={[styles.separator]}
/>
);
}
}
);
return (
<View style={style}>
{children}
</View>
);
};
But this throws an error saying 'React' not found.
However, it works fine with class based components. Following is the code which is working fine.
class ItemsWithSeparator extends React.Component {
render () {
const {children,style} = this.props;
const childrenList = [];
const length = React.Children.count(children);
React.Children.forEach(
children,
(child,ii) => {
childrenList.push(child);
if (ii !== length -1) {
childrenList.push(
<View
key={`separator-${ii}`}
style={[styles.separator]}
/>
);
}
}
);
return (
<View style={style}>
{children}
</View>
);
}
}
Can anyone help me understanding this? TIA!!
Update:
I was just trying few something and apparently got his to work:-
const ItemsWithSeparator = function ({children,style,...props}) {
const childrenList = [];
const length = React.Children.count(children);
React.Children.forEach(
children,
(child,ii) => {
childrenList.push(child);
if (ii !== length -1) {
childrenList.push(
<View
key={`separator-${ii}`}
style={[styles.separator]}
{...props}
/>
);
}
}
);
return (
<View style={style}>
{children}
</View>
);
};
But I am still a bit confused on how is this working. If someone could explain I would really be great.
Here is refactored version so you don't have to do this weird React.Children stuff :D Notice that you can return array when mapping children. There you can make if statements if needed.
const ItemsWithSeparator = ({children, style, ...props}) => {
const finalFields = children.map((child, index) => {
return [
child,
index !== children.length - 1 && (
<View key={index} {...props} style={styles.separator} />
)
];
});
return (
<View style={style}>
{finalFields}
</View>
);
};

Resources