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>
)}</>}
</>
)}
Related
output
code
import React from 'react';
import {
Text,
View,
FlatList,
ScrollView,
SafeAreaView
} from 'react-native';
import { nameData } from './dummydata';
const windowSize = FlatList.length > 50 ? FlatList.length / 4 : 21;
const Main = () => {
return (
<View style={{}}>
<SafeAreaView style={{}}>
<ScrollView style={{}}>
<FlatList
disableVirtualization={true}
//data={nameData.sort((a, b) => a.name.localeCompare(b.name))}
data={nameData.sort(function (a, b) {
return (a.name < b.name) ? -1 : (a.name > b.name) ? 1 : 0;
})}
renderItem={({ item }) => (
<Text style={{ fontSize: 20,marginLeft:10,padding:5 }}>{item.name}</Text>
)}
getItemLayout={(data, index) => ({
length: 80,
offset: 80 * index,
index,
})}
removeClippedSubviews={true}
maxToRenderPerBatch={windowSize}
windowSize={windowSize}
numColumns={1}
keyExtractor={(item, index) => String(index)}
contentContainerStyle={{ paddingBottom: 10 }}
/>
</ScrollView>
</SafeAreaView>
</View>
);
};
export default Main;
I have given the code above
my data is sorted , I want like this if data is started A alphabet then this data contain in A header section if data is started B alphabet then this data contain in B header section.
like this D header contain only D Starting data C header contain only C Starting data
but I don't know how to set header and how to set data in header section.
in sort I want like this data
anybody can give me solution?
thank you!🐼
Method 1
Try this package react-native-section-alphabet-list
A simple React Native component that takes an array of data and renders a SectionList with alphabetically (or custom) sorted data.
Method 2
Update your code like below and try
import React, { useEffect, useState } from "react";
import { Text, View, FlatList, SafeAreaView } from "react-native";
import { nameData } from "./duumydata";
const windowSize = FlatList.length > 50 ? FlatList.length / 4 : 21;
const Main = () => {
let listData = new Array(26).fill([]);
const headerArray = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"];
const [data, setData] = useState([]);
useEffect(() => {
nameData.sort(function (a, b) {
return a.name.toUpperCase() < b.name.toUpperCase()
? -1
: a.name > b.name
? 1
: 0;
});
headerArray.forEach((header, index) => {
nameData.forEach((item) => {
const headerText = item.name.split("")[0].toUpperCase();
if (header == headerText) {
listData[index] = [...listData[index], item];
}
});
});
setData(listData);
}, []);
const renderItem = ({ item, index }) => {
return (
<>
<View style={{ backgroundColor: "grey" }}>
<Text>{headerArray[index]}</Text>
</View>
{item?.map((obj) => renderName(obj))}
</>
);
};
const renderName = (item) => {
return (
<View>
<Text>{item.name}</Text>
</View>
);
};
return (
<View style={{}}>
<SafeAreaView style={{}}>
<FlatList
disableVirtualization={true}
data={data}
renderItem={(item) => renderItem(item)}
getItemLayout={(data, index) => ({
length: 80,
offset: 80 * index,
index
})}
removeClippedSubviews={true}
maxToRenderPerBatch={windowSize}
windowSize={windowSize}
numColumns={1}
keyExtractor={(item, index) => String(index)}
contentContainerStyle={{ paddingBottom: 10 }}
/>
</SafeAreaView>
</View>
);
};
export default Main;
I have a react function component, that I do not want to convert to a class component. I want it to have the functionality so that when it is pressed, the image is changed. However, currently, it is only showing one of the images (checked.png). What am I doing wrong?
const SignUpScreen = () => {
const [check, setCheck] = useState(false);
const changeImage = () => {
setCheck((prevCheck) => !prevCheck);
};
return (
<View>
<Text>
Select the box to change the image
</Text>
<TouchableOpacity onPress={() => changeImage()}>
<Image
source={
{ check } === true
? require("../img/unchecked.png")
: require("../img/checked.png")
}
/>
</TouchableOpacity>
</View>
);
};
export default SignUpScreen;
Remove {} around the check
const SignUpScreen = () => {
const [check, setCheck] = useState(false);
const changeImage = () => {
setCheck((prevCheck) => !prevCheck);
};
return (
<View>
<Text>
Select the box to change the image
</Text>
<TouchableOpacity onPress={() => changeImage()}>
<Image
source={
check === true
? require("../img/unchecked.png")
: require("../img/checked.png")
}
/>
</TouchableOpacity>
</View>
);
};
export default SignUpScreen;
Otherwise it is an object created everytime
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>
)
}
}
This is my code and the error I am getting is in attached picture what is the error in my code. The Purpose of my code is that whenever I click Add new box button a box with random color will be displayed below the previously created box.
import {styleSheets,View,Text,Button,FlatList} from 'react-native';
const ColorF= () => {
const[colors,setColor] = useState([]);
//console.log(colors)
<View>
<Button title = "Add a new box"
onPress = {()=>{
setColor([...colors,randomRgb()])
}}
/>
</View>
return(
<FlatList
data = {colors}
KeyExtractor = {(colors)=>colors.item}
renderItem = {({item})=>{
return <View style ={{height:100,width:100,backgroundColor:randomRgb(item)}}>
}
}
/>
);
}
const randomRgb=()=>{
const red = Math.floor(Math.random()*256);
const green = Math.floor(Math.random()*256);
const blue = Math.floor(Math.random()*256);
return `rgb(${red},${green},${blue})`;
};
export default ColorF;
There were many missing closing tags for components fixed them.
import React, {useState} from 'react';
import {StyleSheet, View, Text, Button, FlatList} from 'react-native';
const App = () => {
const [colors, setColor] = useState([]);
//console.log(colors)
const randomRgb = () => {
const red = Math.floor(Math.random() * 256);
const green = Math.floor(Math.random() * 256);
const blue = Math.floor(Math.random() * 256);
return `rgb(${red},${green},${blue})`;
};
return (
<View>
<Button
title="Add a new box"
onPress={() => {
setColor([...colors, randomRgb()]);
}}
/>
<FlatList
data={colors}
KeyExtractor={colors => colors.item}
renderItem={({item}) => {
return (
<View
style={{
height: 100,
width: 100,
backgroundColor: randomRgb(item),
}}></View>
);
}}
/>
</View>
);
};
export default App;
Try going through and making sure all your elements like <View> and <FlatList> are properly closed and the braces for your function expression like renderItem match up are actually closing up where you think. A hint is that the rest of your code lost its syntax highlighting. Something that can help you is going back through all your lines and trying to fix your indentation so that you can be sure things are inside where you think they should be :)
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>
);
};