Any way to create a "rich" TextInput in React Native? Maybe not a full blown wysiwyg, but maybe just change the text color of various pieces of text; like the #mention feature on Twitter or Facebook.
Solution is that you can use <Text> elements as children in a <TextInput> like this:
<TextInput>
whoa no way <Text style={{color:'red'}}>rawr</Text>
</TextInput>
This question was asked a while ago but I think my answer can help other people looking for how to color the #mention part of a string. I am not sure if the way I did it is clean or the "react" way but here is how I did it:
I take the inputed string and split it with an empty space as the separator. I then loop through the array and if the current item matches the pattern of #mention/#user, it is replaced with the Text tag with the styles applied; else return the item. At the end, I render inputText array (contains strings and jsx elements)inside the TextInput element. Hope this helps!
render() {
let inputText = this.state.content;
if (inputText){
inputText = inputText.split(/(\s)/g).map((item, i) => {
if (/#[a-zA-Z0-9]+/g.test(item)){
return <Text key={i} style={{color: 'green'}}>{item}</Text>;
}
return item;
})
return <TextInput>{inputText}</TextInput>
Have a look at the TokenizedTextExample from the react-native docs. I think that'll get you close to what you're looking to do. The relevant code follows:
class TokenizedTextExample extends React.Component {
state: any;
constructor(props) {
super(props);
this.state = {text: 'Hello #World'};
}
render() {
//define delimiter
let delimiter = /\s+/;
//split string
let _text = this.state.text;
let token, index, parts = [];
while (_text) {
delimiter.lastIndex = 0;
token = delimiter.exec(_text);
if (token === null) {
break;
}
index = token.index;
if (token[0].length === 0) {
index = 1;
}
parts.push(_text.substr(0, index));
parts.push(token[0]);
index = index + token[0].length;
_text = _text.slice(index);
}
parts.push(_text);
//highlight hashtags
parts = parts.map((text) => {
if (/^#/.test(text)) {
return <Text key={text} style={styles.hashtag}>{text}</Text>;
} else {
return text;
}
});
return (
<View>
<TextInput
multiline={true}
style={styles.multiline}
onChangeText={(text) => {
this.setState({text});
}}>
<Text>{parts}</Text>
</TextInput>
</View>
);
}
}
You will have to use regex in order to achieve that behaviour. Someone has already created package for that have a look at react-native-parsed-text
This library allows you to parse a text and extract parts using a RegExp or predefined patterns. Currently there are 3 predefined types: url, phone and email.
Example from their github
<ParsedText
style={styles.text}
parse={
[
{type: 'url', style: styles.url, onPress: this.handleUrlPress},
{type: 'phone', style: styles.phone, onPress: this.handlePhonePress},
{type: 'email', style: styles.email, onPress: this.handleEmailPress},
{pattern: /Bob|David/, style: styles.name, onPress: this.handleNamePress},
{pattern: /\[(#[^:]+):([^\]]+)\]/i, style: styles.username, onPress: this.handleNamePress, renderText: this.renderText},
{pattern: /42/, style: styles.magicNumber},
{pattern: /#(\w+)/, style: styles.hashTag},
]
}
>
Hello this is an example of the ParsedText, links like http://www.google.com or http://www.facebook.com are clickable and phone number 444-555-6666 can call too.
But you can also do more with this package, for example Bob will change style and David too. foo#gmail.com
And the magic number is 42!
#react #react-native
</ParsedText>
Related
enter image description here
https://i.stack.imgur.com/AEGy7.png
I have a TextInput component how can I write content and it suggests the next word that is highlighted as the image
when i enter F and it automatically adds b.com is blacked out
You can have a list with common keywords that you use in your app, something like this:
[
{
"id": 1,
"name": "chicken",
}
{
"id": 2,
"name": "banana",
}
]
Note that ideally you would be getting this data from an API and not manually add it yourself.
Then, make a simple search bar and declare a new variable using useState:
const [filter, setFilter] = useState([])
...
<TextInput style={styles.textinput} placeholder='Search food...' onChangeText={handleChange}/>
The handleChange function is in charge of setting the new data Filter:
const handleChange = (e) => {
const currentFilter = data.filter((item) => {
return item.name.toLowerCase().includes(e.toLowerCase())
})
setFilter(currentFilter)
}
For displaying the data that you want, you can use conditional rendering. Just insert the following snippet inside the View in which you want to display your data:
{
filter.length != 0 && (
<View>
{
filter.map((item, key) => {
return <Text key={item.id} style={styles.textstyle}>{item.name}</Text>
})
}
</View>
)
}
Here is the full code:
export default function FilteringTest() {
const [filter, setFilter] = useState([])
const handleChange = (e) => {
const currentFilter = data.filter((item) => {
return item.name.toLowerCase().includes(e.toLowerCase())
})
setFilter(currentFilter)
}
return (
<View style={styles.background}>
<TextInput style={styles.textinput} placeholder='Search food...' onChangeText={handleChange} />
{
filter.length != 0 && (
<View>
{
filter.map((item, key) => {
return <Text key={item.id} style={styles.textstyle}>{item.name}</Text>
})
}
</View>
)
}
</View>
)
}
As for highlighting the values, you can simply create a new Text on top of the searchBar that contains the closest match. I suggest you read this article on filtering data in react native: https://www.freecodecamp.org/news/how-to-make-a-filter-component-in-react/
TextInput for IOS - https://snack.expo.dev/JrWw1Zj5Z
TextInput for android - https://snack.expo.dev/piRXBQv6aM
For better view you can disable borders on TextInput or make TextInput transparent, and set borders and other styles to Text component
I am new to React Native. I am wondering how to use props to work my code? Here is my code.
const weatherConditions = {
Rain: {
color: '#005BEA',
},
Clear: {
color: '#f7b733',
}
};
const Weather = ({ weather }) => {
return (
<View
style={[
styles.weatherContainer,
{ backgroundColor: weatherConditions.weather.color }
]}/>
);
};
But it does not work. Only the code below works. How to fix this? Help me.
const Weather = ({ weather }) => {
return (
<View
style={[
styles.weatherContainer,
{ backgroundColor: weatherConditions.Rain.color } // or { backgroundColor: weatherConditions.Clear.color }
]}
>
);
};
Your problem is that you are telling your component to look for weatherConditions.weather.color but the key weather is being interpreted literally. The component is looking for the weather key inside weatherConditions and it won't find it.
What you need to do is to do is:
backgroundColor: weatherConditions[weather].color
The brackets ensure weather is interpreted as a variable and not the word weather.
You pass the weather conditions via the prop weather and not weatherConditions. Can you try it with weather.Rain.color? It should work with that.
I have a var str = '<Text> something </Text>', can I render it into a Component?
I tried the following, but it doesn't work :(
var str = '<Text>test</Text>'
render(){
return(
<Text>{str}</Text>
)
}
Is there any way to do this, similar to dangerouslySetInnerHTML in React?
In my project, I get json by fetch just like
{
content:'<p>example</p>'
}
I want to replace the html element p or others into Textand so on by regular expressions, but the result is a string.
I also tried the react-native-html-render but it's document is incomprehensible and doesn't performs well.
You could create a function that converts the inner html of the response you get into a RN Text element.
var str = '<p>something here</p>'
var convertToText = (response) => {
var text = response.split(/[^A-Za-z]/).filter(x => x !== '').slice(1, -1).join(' ') //gives "something here";
return <Text>text</Text>;
}
convertToText(str) === <Text>something here</Text>
Set it as part of your state (e.g., this.state.str). In your constructor, give it a default value (e.g., this.state = {str: "test"}). Then in your function that does the fetch, do setState to change the value (e.g., this.setState({str: response})). Finally, in your render, do this:
render() {
return (
<View style={styles.container}>
<Text>{this.state.str}</Text>
</View>
);
}
var str = <Text>test</Text>;
render() {
return (
<View style={styles.container}>
{str}
</View>
);
}
Unfortunately the tag needs to be incorporated at compile-time, as JSX becomes transpiled into React.createElement calls. Or you can write the React.createElement calls yourself.
For example, you can create a parser than can walk the JSX tree in your server response. Something like
function parseResponseIntoJSXTree (string) {
// very psuedo example:
var type = parseJSXTag(string) // produces `Text`
var props = parseJSXTagProps(string) // produce any attribute=values
var innerContent = parseJSXContent(string) // produces 'something'
return React.createElement(type, props, children)
}
This only scratches the surface, as you'd need to walk the tree if there are child elements deeper than the root node.
Want my horrible, horrible answer?
Include babel in your bundle and do:
var renderableContent = require('babel-core', {presets: ['react-native'])
.transform('<Text>something</Text>')
Note - I highly discourage this, but it technically would work, unless babel requires on node dependencies that won't exist in the runtime (likely).
My requirement is similar, to make dynamic screens with arbitrary components, depending on JSON server response. Here is what I did:
const blockContent = [
{
type: 'text',
content: 'Some title',
size: 20,
color: 'black',
wrapperPadding: 10
},
{
type: 'text',
wrapperPadding: 10,
size: 16,
color: 'red',
content: 'Some text. Some text. Some text. Some text. Some text. '
},
{
type: 'text',
wrapperPadding: 10,
size: 16,
color: 'red',
content: 'Some text. Some text. Some text. Some text. Some text. '
},
{
type: 'link',
content: 'Some link',
size: 16,
color: 'blue',
wrapperPadding: 10,
url: 'http://www.google.com'
}
];
class CustomBlock extends Component {
openURL (url) {
Linking.openURL(url).catch(err => console.error('An error occurred', err));
}
render () {
return (
<View style={styles.container}>
{blockContent.map((item) => {
switch (item.type) {
case 'text':
return (
<View style={{padding: item.wrapperPadding}}>
<Text style={{fontSize: item.size, color: item.color}}>{item.content}</Text>
</View>
);
case 'link':
return (
<TouchableHighlight style={{padding: item.wrapperPadding}} underlayColor="lightgrey" onPress={this.openURL.bind(this, item.url)}>
<Text style={{fontSize: item.size, color: item.color}}>{item.content}</Text>
</TouchableHighlight>
);
}
})}
</View>
);
It's quite easy to declare all components you expect to use like I did with text and link, and to style them as well.
We used ReduxForm in our Web App and we are now trying to build a native app and it seems most of the guys are using tcomb-form.
ReduxForm can be used for native development. Not sure what's the biggest advantage of using tcomb-form.
I tried using it (Didn't explore all the advanced features yet) how ever once we define the schema the basic rendering of the controls is done for you. How ever if you want to customize how a control is displayed i am not sure whether tcomb-form supports it.
Ex:
Here in my Component's render() method:
let Form= tFormNative.form.Form;
let options= {
fields: {
}
};
let username= {
label: I18n.t('LoginForm.username'),
maxLength: 12,
editable: true,
};
let email = {
label: I18n.t('LoginForm.email'),
keyboardType: 'email-address',
editable: true,
};
let password = {
label: I18n.t('LoginForm.password'),
maxLength: 12,
secureTextEntry: true,
editable: true,
};
let registerForm = tFormNative.struct({
username: tFormNative.String,
email: tFormNative.String,
password: tFormNative.String,
passwordReEnter: tFormNative.String,
});
return(
<View style={styles.container}>
<Form
style={styles.textInput}
ref='form'
type={registerForm}
options={options}
/>
</View>
);
Now the Label and Control ( based on the type you provided in struct() ) are created.
How ever Lets say i want to use an Icon aling with the Label for each control i am not sure whether that is permitted.
Appreciate any inputs.
Thanks
Sateesh
If you would like to customize the entire control you can go for a factory, I'll post an example of a Slider i use for number input. I have not tried ReduxForm but i do like tcomb-form a lot, and I don't see anything that should not be doable in terms of customization. Good luck to you!
import React from 'react';
import { View, Text, Slider } from 'react-native';
import t from 'tcomb-form-native';
import Strings from '../config/strings.js';
var Component = t.form.Component;
class TcombSlider extends Component {
constructor(props) {
super(props);
var locals = super.getLocals();
}
getLocals() {
var locals = super.getLocals();
return locals;
}
getTemplate() {
let self = this;
return function (locals) {
var sliderConfig = locals.config.slider;
var stylesheet = locals.stylesheet;
var formGroupStyle = stylesheet.formGroup.normal;
var controlLabelStyle = stylesheet.controlLabel.normal;
var checkboxStyle = stylesheet.checkbox.normal;
var helpBlockStyle = stylesheet.helpBlock.normal;
var errorBlockStyle = stylesheet.errorBlock;
if (locals.hasError) {
formGroupStyle = stylesheet.formGroup.error;
controlLabelStyle = stylesheet.controlLabel.error;
checkboxStyle = stylesheet.checkbox.error;
helpBlockStyle = stylesheet.helpBlock.error;
}
var label = locals.label ? <Text style={controlLabelStyle}>{locals.label}</Text> : null;
var help = locals.config.help ? <Text style={helpBlockStyle}>{locals.config.help}</Text> : null;
var error = locals.hasError && locals.error ? <Text accessibilityLiveRegion="polite" style={[errorBlockStyle, {marginTop: 2}]}>{locals.error}</Text> : null;
return (
<View style={formGroupStyle}>
{label}
<View style={{flex: 1, flexDirection: 'row', justifyContent: 'flex-end', paddingRight: 15}}>
<Text>{locals.value}m</Text>
</View>
<View style={{marginBottom: 5}}>
<Slider
minimumValue={sliderConfig.minimumValue}
maximumValue={sliderConfig.maximumValue}
step={sliderConfig.step}
value={locals.value}
onValueChange={(value) => self._onChange(locals, value)}/>
</View>
{help}
{error}
</View>
);
}
}
_onChange(locals, val) {
locals.onChange(val);
}
}
export default TcombSlider
And use it like this:
const options = {
fields: {
search_range: {
config: {
slider: {
maximumValue: 1000,
minimumValue: 0,
step: 5,
disabled: false,
},
},
factory: TcombSlider,
},
...
},
}
I'm posting this as an example because i had a hard time putting together my first factory.
I would leave this as a comment if I could, but my reputation isn't high enough.
You need to override your form's local template with a custom template which adds an Icon and TextInput for each field.
This gist shows how you can do this.
The React Native TextInput component doesn't support the onSubmitEditing event if it specified as a multi-line input.
Is there a way to detect when the user presses the enter/submit/send (depending on which keyboard layout is specified) button after entering some text?
I realize this is an old post, but I stumbled here from Google and wanted to share my solution. Because of some things that needed to happen in the case of submit, vs simply blurring, I wasn't able to use onBlur to interpret submit.
I utilized an onKeyPress listener to track the Enter key, and then proceeded with the submit. (Note, this is currently only supported in iOS until this PR is merged.)
// handler
onKeyPress = ({ nativeEvent }) => {
if (nativeEvent.key === 'Enter') {
// submit code
}
};
// component
<TextInput
autoFocus={true}
blurOnSubmit={true}
enablesReturnKeyAutomatically={true}
multiline={true}
onChangeText={this.onChangeText}
onKeyPress={this.onKeyPress}
returnKeyType='done'
value={this.props.name}
/>
Note, the blurOnSubmit is still required to prevent the return key from being passed to your onChangeText handler.
On iOS this should work according to the documentation.
Use the onBlur function:
Callback that is called when the text input is blurred
In combination with the ios only blurOnSubmit:
If true, the text field will blur when submitted. The default value is
true for single-line fields and false for multiline fields. Note that
for multiline fields, setting blurOnSubmit to true means that pressing
return will blur the field and trigger the onSubmitEditing event
instead of inserting a newline into the field.
I'll try testing this.
Edit:
Done testing
blurOnSubmit doesn't work like it's supposed to in react-native 0.14.2. Even when set to true, the return/done button and enter key just create a newline and do not blur the field.
I'm not able to test this on newer versions at the moment.
Try it! It works in the middle of the line also!
<TextInput
placeholder={I18n.t('enterContactQuery')}
value={this.state.query}
onChangeText={text => this.setState({ query: text, allowEditing: true })}
selection = {this.state.selection}
onSelectionChange={(event) => this.setState({ cursorPosition: event.nativeEvent.selection, selection: event.nativeEvent.selection, allowEditing: true })}
onSubmitEditing={() => {
const { query, cursorPosition } = this.state;
let newText = query;
const ar = newText.split('');
ar.splice(cursorPosition.start, 0, '\n');
newText = ar.join('');
if (cursorPosition.start === query.length && query.endsWith('\n')) {
this.setState({ query: newText });
} else if (this.state.allowEditing) {
this.setState({
query: newText,
selection: {
start: cursorPosition.start + 1,
end: cursorPosition.end + 1
},
allowEditing: !this.state.allowEditing
});
}
}}
multiline = {true}
numberOfLines = {10}
blurOnSubmit={false}
editable={true}
// clearButtonMode="while-editing"
/>
constructor(props) {
super(props);
this.state = {
query: '',
cursorPosition: [0,0],
selection: null,
allowEditing: true
}
}
constructor () {
super()
this.state = {
text : '',
lastText : '',
inputHeight:40
}
}
writing(text){
this.setState({
text : text
})
}
contentSizeChange(event){
if(this.state.lastText.split("\n").length != this.state.text.split("\n").length){
this.submitTextInput();
}else{
this.setState({
inputHeight : event.nativeEvent.contentSize.height
})
}
}
submitTextInput(){
Alert.alert('submit input : ' + this.state.text);
this.setState({
text : ''
})
}
render() {
return (
<View style={{flex:1,backgroundColor:'#fff'}}>
<TextInput
style={{height:this.state.inputHeight}}
multiline={true}
onChangeText={(text) => this.writing(text)}
onContentSizeChange={(event) => this.contentSizeChange(event)}
onSubmitEditing={() => this.submitTextInput()}
value={this.state.text}
/>
</View>
);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react-dom.min.js"></script>