ReduxForm vs tcomb-form for Native Development - react-native

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.

Related

How to use props in React Native?

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.

TypeError:Super expression must either be null or a function

getting error while making the below page, give me solution for this!!
import React, { Component } from 'react';
import { Text,View ,StyleSheet, TextInput, Button} from 'react-native';
import ValidationComponent from '../index';
export default class Register extends ValidationComponent {
constructor(props) {
super(props);
this.state = {First_name : "First Name",Last_name : 'Last Name', email: "tibtib#gmail.com", password:"****", confirm_password : '****' };
}
I think you've just become a bit confused about passing the variables around. As per our convo in the comments, here's my attempt at what I think you're trying to do (with explanations):
import React, { Component } from 'react';
import { Text, View, StyleSheet, TextInput } from 'react-native';
export default class Register extends Component {
constructor(props) {
super(props);
this.state = {
firstName: { // I have split this so you can store the value and a valid flag conveniently
value: '', // removed initial value and used a placeholder in the element instead
isValid: false
},
lastName: '',
email: 'tibtib#gmail.com',
password: '****',
confirmPassword: '****'
};
}
checkIfValid = (type, value) => { // you can add more checks to this as you add more fields
if (type == 'firstName') return value.length >= 4;
}
_onChangeText = (type, value) => {
// console.log('value =>', value);
const isValid = this.checkIfValid(type, value) // check validity whenever new text is entered
this.setState({ // removed the switch statement and just setState with the isValid variable above
[type]: {
value,
isValid
}
})
};
firstInputStyle = () => { // so we can change the style dynamically depending on state
if (!this.state.firstName.isValid) return styles.firstNameTextInput;
return [styles.firstNameTextInput, styles.isValid]; // if valid, add the isValid style
// you could also generalise this method to work on all input fields
};
render() {
return (
<View style={styles.container}>
<TextInput
style={this.firstInputStyle()} // here we get the style dynamically with the method above
onChangeText={value => this._onChangeText('firstName', value)}
value={this.state.firstName.value} // need to tie the value to our state
placeholder='Enter first name...'
/>
<Text>First name valid = {this.state.firstName.isValid.toString()}</Text>
</View>
);
}
}
const styles = StyleSheet.create({ // some dummy styles I made up
container: {
padding: 20,
paddingTop: 30,
height: '100%'
},
firstNameTextInput: {
borderColor: 'black',
borderWidth: 1,
fontSize: 25,
padding: 10
},
isValid: {
backgroundColor: 'green'
}
});
I hope that make sense... let me know if you have questions!
Note: I've also updated some of your variable names to camelCase as this is more conventional

Error while updating property 'd' of a view managed by: RNSVGPath

I am struggling for last few days with the below error in react native.
My intention:
Dynamically fetch chart data and plot multiple charts on my page.
Whenever I have a succesful fetch the sData[] gets filled. However my chart keeps thrwoing an error:
Error while updating property 'd' of a view managed by: RNSVGPath
null
Attempt to invoke interface method 'int java.Charsequence.length()' on a null object reference
If the fetch fails and my sData is set to default array [5,4,3,2,1] as below in the code, the chart is able to render.
What am i missing/messing? Please help.
import React, { Component } from 'react';
import {AsyncStorage} from 'react-native';
import { LineChart, Grid } from 'react-native-svg-charts';
import { Container, Header, Content, List, ListItem, Text, Left, Right, Body , Button, Title} from 'native-base';
const data = [1,2,3,4,5,6,7];
export default class SomeDetails extends Component {
constructor(props)
{
super(props);
this.state = { 'user': '',
'email': '',
'privLevel': '',
'phNum': '',
UserApiUrl: '<SOMEAPI>',
sData: [],
someData: ''
}
}
componentDidMount() {
this._loadInitialState().done();
}
_loadInitialState = async () => {
var uPhVal = await AsyncStorage.getItem('uPh');
var uEmailVal = await AsyncStorage.getItem('uEmail');
var uPrivVal = await AsyncStorage.getItem('uPlevel');
var uName = await AsyncStorage.getItem('username');
if(uName !== null)
{
this.setState({'user': uName});
this.setState({'phNum': uPhVal});
this.setState({'email': uEmailVal});
this.setState({'privLevel':uPrivVal})
}
var postString = "SOME STRING FOR MY API"
console.log(postString);
response = await fetch(this.state.UserApiUrl, {
method: 'POST',
body: postString
})
res = await response.json();
console.log(res.success);
if (res.success == "true") {
this.setState({ someData: res.someLatestVal });
var dataItems = this.state.someData.split(';');
for(let j=0;j<dataItems.length; j++)
{
var dataI = dataItems[j].split(':');
this.setState({sData: this.state.sData.concat([dataI[0]]) } );
}
}
else {
// console.log("Req: Unable to fetch");
this.setState({sData: [1,2,3,4,5]});
this.setState({loading: true});
}
console.log(this.state.sData);
}
render() {
const { navigation } = this.props;
const someName = navigation.getParam('someName', 'no-name');
return (
<Container>
<Content>
<List>
<ListItem>
<Text>Sensorname: { someName } </Text>
</ListItem>
<LineChart
style={{ height: 70, width: 120 }}
data={ this.state.sData }
svg={{ stroke: 'rgb(134, 65, 244)' }}
contentInset={{ top: 20, bottom: 20 }}
>
<Grid/>
</LineChart>
</List>
</Content>
</Container>
);
}
}
We fixed this by ensuring VictoryLine always gets at least 2 data points. If passed only one data point it may crash.
Here's the working code with a simple check for whether a data set has at least two items:
{dataSetsWithGaps.map(coordList =>
coordList.length < 2 ? null : (
<VictoryLine
key={`line_${coordList[0].x.toString()}`}
interpolation={interpolationMethod}
data={coordList}
/>
),
)}
remove - yarn remove react-native-svg
add - yarn add react-native-svg#9.13
it worked for me

how to pass form data from screen1 to screen2 in react native?

how to pass form data from screen1 to screen2 in react native ? I have following code in scrren1 I want posted amount data in screen2. Please let me know how can I pass data on screen2 and receive it in react native?
import React, { Component } from 'react';
import { Button, View, Text, StyleSheet } from 'react-native';
import t from 'tcomb-form-native'; // 0.6.9
const Form = t.form.Form;
const User = t.struct({
amount: t.String,
});
const formStyles = {
...Form.stylesheet,
formGroup: {
normal: {
marginBottom: 10
},
},
controlLabel: {
normal: {
color: 'blue',
fontSize: 18,
marginBottom: 7,
fontWeight: '600'
},
// the style applied when a validation error occours
error: {
color: 'red',
fontSize: 18,
marginBottom: 7,
fontWeight: '600'
}
}
}
const options = {
fields: {
amount: {
label: "Enter Amount You want to Top up",
error: 'Please add amount to proceed ahead!'
},
},
stylesheet: formStyles,
};
class HomeScreen extends Component {
static navigationOptions = {
title: 'Home',
};
handleSubmit = () => {
const value = this._form.getValue();
console.log('value: ', value);
}
render() {
return (
<View style={styles.container}>
<Form
ref={c => this._form = c}
type={User}
options={options}
/>
<Button
title="Pay Now"
onPress={this.handleSubmit}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
justifyContent: 'center',
marginTop: 50,
padding: 20,
backgroundColor: '#ffffff',
},
});
export default HomeScreen;
It depends if you want to pass data between Parent to Child, Child to Parent or Between Siblingsā€Š
I suggest you to read Passing Data Between React Components, old but this article did help me to understand the logic behind passing data as it's not as easy to implement as in other programming languages.
Excerpt using props:
class App extends React.Component {
render() {
[... somewhere in here I define a variable listName
which I think will be useful as data in my ToDoList component...]
return (
<div>
<InputBar/>
<ToDoList listNameFromParent={listName}/>
</div>
);
}
}
Now in the ToDoList component, use this.props.listNameFromParent to access that data.
You have many ways to send informations from one screen to another in React Native.
eg.
Use React Navigation to navigate between your scenes. You will be able to pass params to your components, which will be accessible in the navigation props when received.
this.props.navigation.navigate({routeName:'sceneOne', params:{name} });
You can also send directly props to a component, and treat them in it. In your render section of your first component, you could have something like this :
<myComponent oneProps={name}/>
In that example, you will receive the props "oneProps" in your second component and you will be able to access it that way :
type Props = {
oneProps: string,
}
class myComponent extends React.Component<Props> {
render() {
console.log('received sent props', oneProps);
return (
<View> // display it
<Text>{this.props.oneProps}</Text>
</View>
);
};
}
These are only two effective solutions, but there are a lot more.
Hope it helped you :)
Have a good day

How to change a string in React-Native into a Component?

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.