
Research
Malicious npm Packages Impersonate Flashbots SDKs, Targeting Ethereum Wallet Credentials
Four npm packages disguised as cryptographic tools steal developer credentials and send them to attacker-controlled Telegram infrastructure.
react-native-stateless-form
Advanced tools
Stateless presentational form components for React Native
It implements the most common pattern of mobile form user interaction by convension over configuration. You'll never have to worry again about scrolling and focusing form fields.
Next
(**)Done
and hides keaboard on return (**)(*) Unless an external keyboard is connected to the device
(**) On Android the return key button is always displayed as Done
for now, since React Native does not support changing it yet. But the behaviour works correctly ;)
This package is inspired by FaridSafi/react-native-gifted-form, and my intention is to merge with it in the future.
The reason for creating a new package is that I want the form components to be presentational only, and not to store state at all. This way we can easily integrate with Redux Form, any other form management tool, or even implement our own form management.
npm install react-native-stateless-form --save
You should add android:windowSoftInputMode="adjustNothing"
attribute to the <activity>
tag with android:name=".MainActivity"
in your AndroidManifest.xml
. Otherwise, it will have duplicate scroll behaviour.
import React, { Component } from 'react-native'
import Icon from 'react-native-vector-icons/MaterialIcons'
import { StatelessForm, InlineTextInput } from 'react-native-stateless-form'
class Form extends Component {
constructor(props, context) {
super(props, context)
this.state = {
name: null,
email: null,
password: null,
}
}
render() {
const { name, email, password } = this.state
const nameValid = (name && name.length > 0 ? true : false)
const emailValid = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(email)
const passwordValid = (password && password.length >= 8 ? true : false)
return (
<StatelessForm style={{
flex: 1,
marginTop: 20,
backgroundColor: 'lightgray',
}}>
<InlineTextInput
label='Name'
placeholder='Tell us your name'
style={{ borderColor: 'gray' }}
labelStyle={{ color: 'dimgray' }}
inputStyle={{ color: 'slategray' }}
messageStyle={{ color: 'red' }}
icon={ <Icon name={'account-circle'} size={18} color={'steelblue'} /> }
validIcon={ <Icon name='check' size={18} color='green' /> }
invalidIcon={ <Icon name='clear' size={18} color='red' /> }
value={name}
valid={nameValid}
message={name && !nameValid ? 'Please fill your name' : null}
onChangeText={(text) => { this.setState({name: text}) }}
/>
<InlineTextInput
label='Email'
placeholder='type@your.email'
autoCorrect={false}
autoCapitalize='none'
keyboardType='email-address'
style={{ borderColor: 'gray' }}
labelStyle={{ color: 'dimgray' }}
inputStyle={{ color: 'slategray' }}
messageStyle={{ color: 'red' }}
icon={ <Icon name={'mail-outline'} size={18} color={'steelblue'} /> }
validIcon={ <Icon name='check' size={18} color='green' /> }
invalidIcon={ <Icon name='clear' size={18} color='red' /> }
value={email}
valid={emailValid}
message={email && !emailValid ? 'Please enter a valid email address' : null}
onChangeText={(text) => { this.setState({email: text}) }}
/>
<InlineTextInput
label='Password'
placeholder='Create a password'
autoCorrect={false}
autoCapitalize='none'
secureTextEntry={true}
style={{ borderColor: 'gray' }}
labelStyle={{ color: 'dimgray' }}
inputStyle={{ color: 'slategray' }}
messageStyle={{ color: 'red' }}
icon={ <Icon name={'vpn-key'} size={18} color={'steelblue'} /> }
validIcon={ <Icon name='check' size={18} color='green' /> }
invalidIcon={ <Icon name='clear' size={18} color='red' /> }
value={password}
valid={passwordValid}
message={password && !passwordValid ? 'Password too short' : null}
onChangeText={(text) => { this.setState({password: text}) }}
/>
</StatelessForm>
)
}
}
import { AppRegistry } from 'react-native'
AppRegistry.registerComponent('Form', () => Form)
import React, { Component, PropTypes } from 'react-native'
import Icon from 'react-native-vector-icons/MaterialIcons'
import { StatelessForm, InlineTextInput } from 'react-native-stateless-form'
class FormInput extends Component {
// You MUST implement focus and blur methods for your component to work
focus() {
this.refs.input.focus()
}
blur() {
this.refs.input.blur()
}
render() {
const { iconName } = this.props
return (
<InlineTextInput
ref='input' // This is necessary for focus() and blur() implementation to work
style={{ borderColor: 'gray' }}
labelStyle={{ color: 'dimgray' }}
inputStyle={{ color: 'slategray' }}
messageStyle={{ color: 'red' }}
icon={ <Icon name={iconName} size={18} color={'steelblue'} /> }
validIcon={ <Icon name='check' size={18} color='green' /> }
invalidIcon={ <Icon name='clear' size={18} color='red' /> }
{ ...this.props }
/>
)
}
}
// You MUST add these two props to propTypes in order to have auto-focus and auto-scroll working
FormInput.propTypes = {
value: PropTypes.string,
valid: PropTypes.bool,
}
class Form extends Component {
constructor(props, context) {
super(props, context)
this.state = {
name: null,
email: null,
password: null,
}
}
render() {
const { name, email, password } = this.state
const nameValid = (name && name.length > 0 ? true : false)
const emailValid = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(email)
const passwordValid = (password && password.length >= 8 ? true : false)
return (
<StatelessForm style={{flex: 1, marginTop: 20, backgroundColor: 'lightgray'}}>
<FormInput
label='Name'
placeholder='Tell us your name'
iconName='account-circle'
value={name}
valid={nameValid}
message={name && !nameValid ? 'Please fill your name' : null}
onChangeText={(text) => { this.setState({name: text}) }}
/>
<FormInput
label='Email'
placeholder='type@your.email'
autoCorrect={false}
autoCapitalize='none'
keyboardType='email-address'
iconName='mail-outline'
value={email}
valid={emailValid}
message={email && !emailValid ? 'Please enter a valid email address' : null}
onChangeText={(text) => { this.setState({email: text}) }}
/>
<FormInput
label='Password'
placeholder='Create a password'
autoCorrect={false}
autoCapitalize='none'
secureTextEntry={true}
iconName='vpn-key'
value={password}
valid={passwordValid}
message={password && !passwordValid ? 'Password too short' : null}
onChangeText={(text) => { this.setState({password: text}) }}
/>
</StatelessForm>
)
}
}
import { AppRegistry } from 'react-native'
AppRegistry.registerComponent('Form', () => Form)
import React, { Component, PropTypes } from 'react-native'
import Icon from 'react-native-vector-icons/MaterialIcons'
import { StatelessForm, InlineTextInput } from 'react-native-stateless-form'
import { validate } from 'validate-model'
const UserValidators = {
name: {
title: 'Name',
validate: [{
validator: 'isLength',
arguments: [1, 255],
}]
},
email: {
title: 'Email',
validate: [{
validator: 'isLength',
arguments: [1, 255],
},
{
validator: 'isEmail',
message: '{TITLE} must be valid',
}]
},
password: {
title: 'Password',
validate: [{
validator: 'isLength',
arguments: [8, 255],
message: '{TITLE} is too short',
}]
},
}
class FormInput extends Component {
focus() {
this.refs.input.focus()
}
blur() {
this.refs.input.blur()
}
render() {
const { iconName, name, value } = this.props
const { valid, messages } = validate(UserValidators[name], value)
const message = (messages && messages.lenght > 0 ? messages[0] : null)
return (
<InlineTextInput
ref='input'
style={{ borderColor: 'gray' }}
labelStyle={{ color: 'dimgray' }}
inputStyle={{ color: 'slategray' }}
messageStyle={{ color: 'red' }}
icon={ <Icon name={iconName} size={18} color={'steelblue'} /> }
validIcon={ <Icon name='check' size={18} color='green' /> }
invalidIcon={ <Icon name='clear' size={18} color='red' /> }
valid={valid}
message={message}
{ ...this.props }
/>
)
}
}
FormInput.propTypes = {
value: PropTypes.string,
valid: PropTypes.bool,
}
class Form extends Component {
constructor(props, context) {
super(props, context)
this.state = {
name: null,
email: null,
password: null,
}
}
render() {
const { name, email, password } = this.state
return (
<StatelessForm style={{flex: 1, marginTop: 20, backgroundColor: 'lightgray'}}>
<FormInput
name='name'
label='Name'
placeholder='Tell us your name'
iconName='account-circle'
value={name}
onChangeText={(text) => { this.setState({name: text}) }}
/>
<FormInput
name='email'
label='Email'
placeholder='type@your.email'
autoCorrect={false}
autoCapitalize='none'
keyboardType='email-address'
iconName='mail-outline'
value={email}
onChangeText={(text) => { this.setState({email: text}) }}
/>
<FormInput
name='password'
label='Password'
placeholder='Create a password'
autoCorrect={false}
autoCapitalize='none'
secureTextEntry={true}
iconName='vpn-key'
value={password}
onChangeText={(text) => { this.setState({password: text}) }}
/>
</StatelessForm>
)
}
}
import { AppRegistry } from 'react-native'
AppRegistry.registerComponent('Form', () => Form)
import React, { Component, PropTypes } from 'react-native'
import Icon from 'react-native-vector-icons/MaterialIcons'
import { StatelessForm, InlineTextInput } from 'react-native-stateless-form'
import { validateAll } from 'validate-model'
import { Provider } from 'react-redux'
import { createStore, combineReducers, applyMiddleware } from 'redux'
import { reduxForm, reducer as formReducer } from 'redux-form'
import createLogger from 'redux-logger'
const UserValidators = {
name: {
title: 'Name',
validate: [{
validator: 'isLength',
arguments: [1, 255],
}]
},
email: {
title: 'Email',
validate: [{
validator: 'isLength',
arguments: [1, 255],
},
{
validator: 'isEmail',
message: '{TITLE} must be valid',
}]
},
password: {
title: 'Password',
validate: [{
validator: 'isLength',
arguments: [8, 255],
message: '{TITLE} is too short',
}]
},
}
const validate = values => {
const validation = validateAll(UserValidators, values)
if (!validation.valid) return validation.messages
return {}
}
class FormInput extends Component {
focus() {
this.refs.input.focus()
}
blur() {
this.refs.input.blur()
}
render() {
const { iconName, name, value, error } = this.props
const message = ( error && error.length > 0 ? error[0] : null)
return (
<InlineTextInput
ref='input'
style={{ borderColor: 'gray' }}
labelStyle={{ color: 'dimgray' }}
inputStyle={{ color: 'slategray' }}
messageStyle={{ color: 'red' }}
icon={ <Icon name={iconName} size={18} color={'steelblue'} /> }
validIcon={ <Icon name='check' size={18} color='green' /> }
invalidIcon={ <Icon name='clear' size={18} color='red' /> }
message={message}
{ ...this.props }
/>
)
}
}
FormInput.propTypes = {
value: PropTypes.string,
valid: PropTypes.bool,
}
class Form extends Component {
render() {
const { fields: { name, email, password } } = this.props
return (
<StatelessForm style={{flex: 1, marginTop: 20, backgroundColor: 'lightgray'}}>
<FormInput
name='name'
label='Name'
placeholder='Tell us your name'
iconName='account-circle'
{ ...name }
/>
<FormInput
name='email'
label='Email'
placeholder='type@your.email'
autoCorrect={false}
autoCapitalize='none'
keyboardType='email-address'
iconName='mail-outline'
{ ...email }
/>
<FormInput
name='password'
label='Password'
placeholder='Create a password'
autoCorrect={false}
autoCapitalize='none'
secureTextEntry={true}
iconName='vpn-key'
{ ...password }
/>
</StatelessForm>
)
}
}
Form = reduxForm({
form: 'user',
fields: ['name', 'email', 'password'],
validate
})(Form);
const reducers = {
form: formReducer
}
const reducer = combineReducers(reducers)
const createStoreWithMiddleware = applyMiddleware(createLogger())(createStore)
function configureStore(initialState) {
return createStoreWithMiddleware(reducer, initialState)
}
const store = configureStore()
const Root = () => (
<Provider store={store}>
<Form />
</Provider>
)
import { AppRegistry } from 'react-native'
AppRegistry.registerComponent('Form', () => Root)
A wrapper that will manage auto-focusing and auto-scrolling for its children components
Property | Type | Default | Description |
---|---|---|---|
style | style | {} | Style for the form wrapper |
+ Any other ScrollView prop you wish to pass.
Property | Type | Default | Description |
---|---|---|---|
label | string | 'Use label prop' | Label for the text input |
value | string | null | Value for the text input |
valid | boolean | false | Whether the value is valid or not |
message | string | null | Validation message to be shown |
style | style | {} | Style changes to the main ScrollView |
iconStyle | style | {} | Style changes to the icon View |
labelStyle | style | {} | Style changes to the label Text |
inputStyle | style | {} | Style changes to the TextInput |
messageStyle | style | {} | Style changes to the validation message Text |
icon | element | null | Any react component to be used as icon |
validIcon | element | null | Any react component to be used as icon when valid. Requires icon prop |
invalidIcon | element | null | Any react component to be used as icon when invalid. Requires icon prop |
+ Any other TextInput prop you wish to pass.
My intention is to implement most of FaridSafi/react-native-gifted-form's components. But I'll do each one only when I need it in a real project, so it might take some time.
PR's are very much welcome!
Any react component can be rendered inside Stateless Form as a component. But there is a special case below:
If you want your component to receive focus when previous component finished editing, you must implement the following pattern:
focus()
method.blur()
method.onSubmitEditing
or equivalent and call this.props.onNextInputFocus(this.props.nextInput, this)
so StatelessForm can focus the next input or blur the current input.valid
and value
on its propTypes
. This is how StatelessForm
will recognize it as a focusable and/or scrollable input component. It is important that only focusable or scrollable components have these props on propTypes
.If you want your component to receive scroll when showing keyboard, you must implement the following pattern:
onFocus
and call this.props.onFocus(scrollTo)
on focus. scrollTo
must be your component's y
position.y
position using onLayout
prop. Check InlineTextInput for references on how to implement it.onBlur
and call this.props.onBlur
on blur.valid
and value
on its propTypes
.Please create issues and send pull requests!
FAQs
Stateless presentational form components for React Native
The npm package react-native-stateless-form receives a total of 3 weekly downloads. As such, react-native-stateless-form popularity was classified as not popular.
We found that react-native-stateless-form demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Four npm packages disguised as cryptographic tools steal developer credentials and send them to attacker-controlled Telegram infrastructure.
Security News
Ruby maintainers from Bundler and rbenv teams are building rv to bring Python uv's speed and unified tooling approach to Ruby development.
Security News
Following last week’s supply chain attack, Nx published findings on the GitHub Actions exploit and moved npm publishing to Trusted Publishers.