React Native : replace a string with a Text component - react-native

I am trying to convert some homemade tags/bbcode and for doing so I need in some case not to return a string but a text component, I am trying to do that with this small function but the issue is that when I render it it display [object Object] instead of the component.
helper.js
import { Text } from "react-native";
const decodeTags = (string) => {
string = string.replace(
/\[b\](.+)\[\/b\]/isu,
<Text style={{ fontWeight: "bold" }}>$1</Text>
);
profilBio.js
<Text style={styles.bio}>
{decodeTags(props.profil.bio)}
</Text>
Render
[object Object] (instead of some text with a part in bold)

If the request is how to update styles conditionally;
To update styles conditionally;
<Text style={(this.state.myCondition == true) ? styles.bio_font_weight : styles.bio}>{decodeTags(props.profil.bio)}</Text>
And update decodeTags() to return only the data, not a Text component.

You'll need some pre-processor function which would basically split the string at the string which you need to make bold. After that, just basic string substring operation and concatenating the parts.
Ex:
const highlightKeyword = useCallback((originalString = '', searchQuery) => {
let s = originalString;
let searchStartIndex = s.indexOf(searchQuery);
return (
<Text>
<Text style={Styles.text}>{s.substr(0, searchStartIndex)}</Text>
<Text style={[Styles.text, {color: Colors.RED}]}>{searchQuery}</Text>
<Text style={Styles.text}>
{s.substr(searchStartIndex + searchQuery.length, s.length)}
</Text>
</Text>
);
}, []);
highlightKeyword(props.profil.bio,searchQuery)

Related

How to Get Multiple TextInput Values in single variable in React-Native?

How to get values from multiple TextInput fields coming from array passing by map function to single variable by entering text in them? When i try to enter text in second TextInput it replaces the value which i stored from first TextInput.
Secondly instead of this, if i use push() function, it doesn't give desired output. What I exactly want is that to get the values of these three InputText and to store in single variable using single OnChangeEvent (as I am using Map() function).
Below is the sample of code on which i am working in React-Native.
this.state={
checkboxAttCollection:[{"Id": 10, "AttibuteCollectionName": "PowerTest"}, {"Id": 22, "AttibuteCollectionName": "36W"}, {"Id": 23, "AttibuteCollectionName": "Test123"}],
getInputText:'',
getInputvariables:[],
onChangeTextComp=(TextValue,index)=>{
this.state.getInputText=TextValue
console.log("******",this.state.getInputvariables)
this.state.getInputvariables.push(this.state.getInputText)
console.log("******",this.state.getInputvariables)
{this.state.checkboxAttCollection.map((item, i) =>
return (<View key={item.AttibuteCollectionId} style={{ flexDirection: 'row',}}>
<TextInput style={{height:hp('5%'),width:wp('60%'),backgroundColor:'red',borderBottomWidth:2,paddingVertical:0}}
onChangeText={(text)=>this.onChangeTextComp(text,i)}
MultiInputText={true}/>
{/* </View> */}
</View>)
})}
Try this way
this.state={
......
inputVlaues: []
}
onChangeTextComp=(value,index)=>{
const inputData = [...this.state.inputVlaues];
inputData[index] = value;
this.setState(inputVlaues: inputData);
}
{this.state.checkboxAttCollection.map((item, i) =>
return
(<View key={item.AttibuteCollectionId} style={{ flexDirection: 'row',}}>
<TextInput
onChangeText={(text)=>this.onChangeTextComp(text,i)}
......
value={this.state.inputVlaues[i] || ""}
/>
</View>)
})}
as an option you can generate event handlers dynamically
const [form, setForm] = useState({})
const createHandler = (fieldName) => {
return (newFieldValue) => {
setForm((oldValue) => {
return {
...oldValue,
[fieldName]: newFieldValue
}
})
}
}
return (
<>
<TextInput onChange={createHandler('name')} />
<TextInput onChange={createHandler('surname')} />
</>
)

react-native customize text elipsis to add "More" after elipsis

Following is from instagram which is known for using react-native
As you can see there is a text (더보기, which means More) after the elipsis (...)
How can I simulate that too?
Well, that is one of those things that on the surface looks easy enough, but actually is a complicated endeavor.
Due to this thing over here: https://github.com/facebook/react-native/issues/22811 you won't be able to nest the <Text> tag and use the numberOfLines prop to achieve what you want.
In the past I've tried to deal with something similar: React Native chat bubble flexbox similar to whatsapp
Back then there was no onTextLayout function(or at least I didn't know about it). The onTextLayout callback on the Text component gives you a line prop. The line is an array that contains information about every line of text.
You could use this to place the more button where you want.
const MoreInfo = text => {
const [moreLeft, setMoreLeft] = React.useState(0)
const [moreTop, setMoreTop] = React.useState(0)
return (
<View style={{ marginTop: 20 }}>
<Text
numberOfLines={2}
ellipsizeMode={"tail"}
onTextLayout={({ nativeEvent: { lines } }) => {
const width = lines[lines.length - 1].width
const height = lines[lines.length - 1].y
setMoreTop(height)
setMoreLeft(width)
}}
>
{text}
</Text>
<Text
style={{
backgroundColor: "white",
position: "absolute",
left: moreLeft - 30,
top: moreTop,
}}
>
... More
</Text>
</View>
)
}
Obviously the above implementation is not perfect. As you can see I just fake the ... by placing them in the More button. Sometimes it looks good, othertimes it cuts off the last letter in the string in the middle. But it serves to show the direction you need to go to solve this.
Some ideas:
inspect the lines array - if it has 2 lines is the last line width smaller than the first line width. Will your "More" text fit in the width difference? If yes, then just set the left position to the width.
if the second line is as long as the first one, then the ellepsis is at the end and you'll need to resort to the "... More" trick.
Here is a quick workaround. Create a function which takes text as an argument and show truncated text with a more button. Upon click, we will render full text with a less button.
const MoreLessComponent = ({ truncatedText, fullText }) => {
const [more, setMore] = React.useState(false);
return (
<Text>
{!more ? `${truncatedText}...` : fullText}
<TouchableOpacity onPress={() => setMore(!more)}>
<Text>{more ? 'less' : 'more'}</Text>
</TouchableOpacity>
</Text>
);
};
const MoreInfo = (text, linesToTruncate) => {
const [clippedText, setClippedText] = React.useState(false);
return clippedText ? (
<MoreLessComponent truncatedText={clippedText} fullText={text} />
) : (
<Text
numberOfLines={linesToTruncate}
ellipsizeMode={'tail'}
onTextLayout={(event) => {
//get all lines
const { lines } = event.nativeEvent;
//get lines after it truncate
let text = lines
.splice(0, linesToTruncate)
.map((line) => line.text)
.join('');
//substring with some random digit, this might need more work here based on the font size
//
setClippedText(text.substr(0, text.length - 9));
}}>
{text}
</Text>
);
};
Now call it like this
<View>
{MoreInfo('Change code in the editor and watch it change on your phone! Save to get a shareable url.')}
</View>
Here is the snack https://snack.expo.io/#saachitech/react-native-more-less

Updating button state which is clicked, inside RenderItem of Flat-list, in React Native

I am new to React Native and trying to accomplish something like below, where simple list from server is rendering in a list with a button. Button, upon click, will be disabled and opacity will be changed.
I am able to create the UI but when I click any button that says Join, previous clicked button resets it state to original. In other words, only one button displays clicked state all the time.
so my code is something like
constructor(props){
super(props);
this.state = {selectedIndices: false, groupsData: ''};
}
Flatlist inside render method looks like
<FlatList style={styles.list}
data={this.state.groupsData}
keyExtractor={(groups) => {
return groups.groupId.toString();
}}
renderItem={(item, index) => {
return this.renderGroupItem(item);
}}/>
RenderGroupItem
renderGroupItem = ({item} )=>(
<GroupItem group = {item} style={{height: '10%'}} onPress = {() => this.onJoinButtonPress(item)}
index = {this.state.selectedIndices}/>
)
onJoinButtonPress
onJoinButtonPress = (item) =>{
this.setState({selectedIndices: true});
}
GroupItem
render(){
if(this.props.group.groupId === this.props.index){
return(
<View style = {[styles.container]}>
<Image source={{uri: 'some Image Url'}} style={styles.roundImage}/>
<Text style={styles.groupText}>{this.props.group.name}</Text>
<View >
<TouchableOpacity style = {[styles.button, {opacity: 0.4}]} activeOpacity = { .5 } onPress = {this.props.onPress}
disabled = {true}>
<Text style = {{color: 'white', fontSize: 12}}>Joined</Text>
</TouchableOpacity>
</View>
</View>
);
}else{
return(
<View style = {styles.container}>
<Image source={{uri: 'Some Image Url'}} style={styles.roundImage}/>
<Text style={styles.groupText}>{this.props.group.name}</Text>
<View >
<TouchableOpacity style = {styles.button} activeOpacity = { .5 } onPress = {this.props.onPress}>
<Text style = {{color: 'white', fontSize: 12}}>Join</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
Now I know that I need to pass an array or hasmap which contains mapping of items that have been clicked but I dont know how to do that. Need desperate help here.
I was able to overcome above problem after maintaining a boolean in the groupsData. Upon selection, I update a boolean "groupsJoined" in groupsData and update the state of groupsData which will invoke render. Inside GroupsItem class, I added a check that if data from props has joinedGroups as true then render selected state else non selected state.
Courtesy https://hackernoon.com/how-to-highlight-and-multi-select-items-in-a-flatlist-component-react-native-1ca416dec4bc

React native:how to get Text value on a button click?

I have a Text, and I want to get text value on click.
for example
click()
{
// how to get text value here
}
<Text style={{color: 'red,textAlign:'center'}} onPress={this.click.bind(this)}>
Name
</Text>
Can be done this way too...
<Text onPress={(event) => console.log(event._dispatchInstances.memoizedProps.children)} >{value}</Text>
You may use ref attribute to access Text's value.
<Text ref='myText'>This is my text</Text>
<Button onPress={()=>alert(this.refs.myText.props.children)} title='Press Me'/>
You can do maintaining the text inside the state and get the text value when button is clicked.
export default class SampleApp extends Component{
constructor(props){
super(props);
this.state = {
titleText: "Click to get text! - ",
count:1
};
}
render() {
return (
<View style={{flex:1}}>
<Text style={{color:'black'}} onPress={()=>{this.onPressTitle()}}>
{this.state.titleText}
</Text>
</View>
);
}
onPressTitle(){
alert(this.state.titleText+this.state.count);
this.setState({count:this.state.count+1});
}
}
Works fine with dynamic text.
For me, it has worked with console.log(event._dispatchInstances.memoizedProps.children[0].props.children)

React Native - Trying to print depending on the state of index

I am trying to loop through this array and print out the quote, depending on if it is saved as true or false.
At the moment, it just prints out in the JSON array. I'm new to react native, so not 100% certain of the behaviour of map.
var savedQuote = quotes.quotesArray.map(quote => quote.isSaved)
{
quotes.quotesArray.map((quote, i) => {
if(savedQuote){
return(
<TouchableOpacity key={i} style = {styles.border}>
<Text style = {styles.text}>
i: {i}. {quotes.quotesArray[i].preview}
</Text>
</TouchableOpacity>
)
}
else{
return <Text key = {i}>{i}Nada</Text>
}
})
}
I don't understand why you have two map in your code. One should suffice :
quotes.quotesArray.map((quote, i) => {
if(quote.isSaved){
return(
<TouchableOpacity key={i} style = {styles.border}>
<Text style = {styles.text}>
i: {i}. {quote.preview}
</Text>
</TouchableOpacity>
)
} else {
return <Text key = {i}>{i}Nada</Text>
}
});
This will loop through quotes and either return one node or the either if quote.isSaved is true or not.
you could save them to a new array if you assign it to a new variable ex: var savedQuotes = ... or use it inside your render function :
render() {
return (
<View>
{quotes.quotesArray.map...}
</View>
);
}
The map() method creates a new array with the results of calling a
provided function on every element in this array.
Here is more information: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/map
Here are some examples from the react documentation : https://facebook.github.io/react/docs/lists-and-keys.html (which should be close enough to react-native)