I'm using DatePicker with ReduxForm. However, when I click submit button, the input value from Date Picker not pass to Redux Form.
I've search around and come across the answer from this (my code of renderDatePicker comes from there) but it still doesn't work for me.
My demo of the form on my Simulator:
Here's my code:
import React, { Component } from 'react';
import {
View, Text, Button, Icon, Container, Item,
Input, Label, Content, Form, Picker, Footer, DatePicker
} from 'native-base';
import { Field, reduxForm } from 'redux-form';
import { addTransactionItem } from '../redux/ActionCreators';
import moment from 'moment';
import { connect } from 'react-redux';
const mapDispatchToProps = dispatch => ({
addTransactionItem: (transactionItem) => dispatch(addTransactionItem(transactionItem))
})
class AddTransaction extends Component {
constructor(props) {
super(props);
this.renderField = this.renderField.bind(this);
this.submit = this.submit.bind(this);
this.renderDatePicker = this.renderDatePicker.bind(this);
}
renderDatePicker = ({ input, placeholder, defaultValue, meta: { touched, error }, label ,...custom }) => (
<Item>
<Label>{label}</Label>
<DatePicker {...input} {...custom} dateForm="MM/DD/YYYY"
onChange={(value) => input.onChange(value)}
autoOk={true}
selected={input.value ? moment(input.value) : null} />
{touched && error && <span>{error}</span>}
</Item>
);
submit = values => {
alert(`The values are ${JSON.stringify(values)}`)
const transactionItem = JSON.parse(JSON.stringify(values))
this.props.addTransactionItem(transactionItem);
const { navigate } = this.props.navigation;
navigate('Home');
}
render() {
const { handleSubmit } = this.props
return (
<>
<Form>
<Field keyboardType='default' label='Date' component={this.renderDatePicker} name="date" />
</Form>
<Button full light onPress={handleSubmit(this.submit)}>
<Text>Submit</Text>
</Button>
</>
);
}
}
AddTransaction = connect(null, mapDispatchToProps)(AddTransaction);
export default reduxForm({
form: 'addTransaction',
})(AddTransaction);
I think this is because you do not have "change" attribute in the Field component.
Try to add change function as shown below:
renderDatePicker = (
{
input,
placeholder,
defaultValue,
meta: { touched, error },
label ,
...custom,
change
}
) => (
<Item>
<Label>{label}</Label>
<DatePicker {...input} {...custom} dateForm="MM/DD/YYYY"
onDateChange={change}
autoOk={true}
selected={input.value ? moment(input.value) : null} />
{touched && error && <span>{error}</span>}
</Item>
);
render() {
const { handleSubmit, change } = this.props
return (
<>
<Form>
<Field
keyboardType='default'
label='Date'
component={this.renderDatePicker}
name="date"
change={change}
/>
</Form>
<Button full light onPress={handleSubmit(this.submit)}>
<Text>Submit</Text>
</Button>
</>
);
}
Hope it will work for you.
I see that there is no onChange listener for DatePicker. May be you should use onDateChange. http://docs.nativebase.io/Components.html#picker-input-headref
Related
using react-admin i created an app with sample list with many pages then i ran it. if i opened another page in the list and chose to edit a row or to delete it, the task done but the list is redirected to the first page and this is not good for user experience. if the user want to review multiple rows and edit them this will oblige him to return to the page each time he made edit. i am not sure if this is a how to question or a bug or feature that should be posted in github. i tested it in multiple react-admin versions 3.6.0, 3.5.5, 3.0.0 and the same behavior appeared.
// in src/App.js
import * as React from "react";
import { Admin, Resource } from "react-admin";
import jsonServerProvider from "ra-data-json-server";
import CommentList from "./comments";
const dataProvider = jsonServerProvider("https://jsonplaceholder.typicode.com");
const App = () => (
<Admin dataProvider={dataProvider}>
<Resource name="comments" list={CommentList} />
</Admin>
);
export default App;
import * as React from "react";
import {
List,
Datagrid,
TextField,
ReferenceField,
EmailField
} from "react-admin";
import { Fragment } from "react";
import { BulkDeleteButton } from "react-admin";
import ResetViewsButton from "./ResetViewsButton";
const PostBulkActionButtons = props => (
<Fragment>
<ResetViewsButton label="Reset Views" {...props} />
{/* default bulk delete action */}
<BulkDeleteButton {...props} />
</Fragment>
);
const CommentList = props => (
<List {...props} bulkActionButtons={<PostBulkActionButtons />}>
<Datagrid rowClick="edit">
<ReferenceField source="postId" reference="posts">
<TextField source="id" />
</ReferenceField>
<TextField source="id" />
<TextField source="name" />
<EmailField source="email" />
<TextField source="body" />
</Datagrid>
</List>
);
export default CommentList;
import * as React from "react";
import { Button, useUpdateMany, useNotify, useUnselectAll } from "react-admin";
import { VisibilityOff } from "#material-ui/icons";
const ResetViewsButton = props => {
const notify = useNotify();
const unselectAll = useUnselectAll();
console.log(props.selectedIds);
console.log(props.basePath);
const [updateMany, { loading }] = useUpdateMany(
"comments",
props.selectedIds,
{ emails: "" },
{
onSuccess: () => {
notify("comments updated");
unselectAll("comments");
},
onFailure: error => notify("Error: comments not updated", "warning")
}
);
return (
<Button
label="simple.action.resetViews"
disabled={loading}
onClick={updateMany}
>
<VisibilityOff />
</Button>
);
};
export default ResetViewsButton;
You can use "useRedirect" If I am not getting you wrong. You want to redirect after edit info.
import { useRedirect } from 'react-admin';
const ResetViewsButton = props => {
const notify = useNotify();
const redirectTo = useRedirect();
const unselectAll = useUnselectAll();
console.log(props.selectedIds);
console.log(props.basePath);
const [updateMany, { loading }] = useUpdateMany(
"comments",
props.selectedIds,
{ emails: "" },
{
onSuccess: () => {
notify("comments updated");
unselectAll("comments");
redirectTo('/url');
},
onFailure: error => notify("Error: comments not updated", "warning")
}
);
return (
<Button
label="simple.action.resetViews"
disabled={loading}
onClick={updateMany}
>
<VisibilityOff />
</Button>
);
};
export default ResetViewsButton;
it was a bug. wait version 3.6.1
In React Native 16.
Get input value
<Input
...
onChange={event => getValue(event)}
/>
Output value
const getValue = event => {
console.log(event.nativeEvent.text);
};
The Input component from react-native-elements is basically rendering the TextInput component. So you can use onChangeText instead of onChange like
import React, { Component } from 'react';
import { Input } from 'react-native-elements';
export default function UselessTextInput() {
const [value, onChangeText] = React.useState('Useless Placeholder');
return (
<Input
onChangeText={text => onChangeText(text)}
value={value}
/>
);
}
Hope this helps
Set value to the state.
const getValue = event => {
this. setState({text: event.nativeEvent.text});
};
I'm new to react native.
I've created a react native app and my first screen is a login screen. I'm using onChangeText to update state vars with username and password and this works great initially.
However on "logout" when I pop back to the login screen. The inputs still have my username and password in. However the state vars are now back to null.
I've tried setting value to {this.state.username} for the input but this just causes a depth error on state after 2 input presses so doesn't work.
Am I missing something?
import React, { Component } from 'react';
import { View, Text, StyleSheet, Image, Alert, AsyncStorage, Linking } from 'react-native';
import { Input, Left, Spinner, Container, Item, Form, Header, Content, Label, Button } from 'native-base'
export default class Login extends Component {
state = { username: "", password: "", isLoaded: true }
static navigationOptions = {
header: null
}
constructor(props) {
super()
this.state.isLoaded = false
AsyncStorage.getItem("loggedIn").then(res => {
if (res === "true") {
this.props.navigation.navigate('List')
}
else {
this.setState({isLoaded: true})
}
})
}
checkLogin() {
if ((!this.state.username) || (!this.state.password)) {
Alert.alert('Error', 'Username/Password combination unknown', [{
text: 'Okay'
}])
return
}
....... snip ......
if (response === false) {
Alert.alert('Error', 'Username/Password combination unknown', [{
text: 'Okay'
}])
}
else {
AsyncStorage.setItem('user', JSON.stringify(response));
AsyncStorage.setItem('loggedIn', "true");
this.setState({username: null, password: null})
this.props.navigation.navigate('List')
}
}
}
render()
{
if (this.state.isLoaded == false) {
return (
<Container>
<Spinner />
</Container>
)
}
return (
<Container>
<Content>
<Image source={require('../../assets/logo.jpg')}/>
<Form>
<Item floatingLabel>
<Label>Username</Label>
<Input
autoCapitalize='none'
clearButtonMode='always'
onChangeText={text => this.setState({username:text})} />
</Item>
<Item floatingLabel>
<Label>Password</Label>
<Input
secureTextEntry={true}
clearButtonMode='always'
onChangeText={text => this.setState({password: text})} />
</Item>
<Button primary onPress={_ => this.checkLogin()}>
<Text style={styles.loginButtonText}>Login</Text>
</Button>
</Form>
</Content>
</Container>
);
}
}
You can use direct manipulation method.
Try passing ref to Input like ref={ (c) => this._input = c } and then calling the setNativeProps function this._input.setNativeProps({text:''})
I am also using react navigation and face similar issue.
I fixed as below :
import { NavigationEvents } from "react-navigation";
class ... {
onStartScreenFocus = ()>={
this.setState({
username: "", password: ""
})
}
render(){
return(
<View>
<NavigationEvents
onWillFocus={() => this.onStartScreenFocus()}
onDidBlur={() => this.onDidScreenBlur()} />
<View>
)
}
}
I have form with only one TextInput which is made using redux-form. I am checking (!meta.active) to show validation message, since focus is not changing even on button click from TextInput, meta.active is always true and validation message does not shows up.
export default function MTTextInput(props) {
const { input, label, meta, ...inputProps } = props;
var hasError = false;
if (meta.error !== undefined && meta.touched && !meta.active) {
hasError = true;
}
return (
<Item fixedLabel error={hasError} ><Label>{label}</Label>
<Input
{...inputProps}
onChangeText={input.onChange}
onBlur={input.onBlur}
onFocus={input.onFocus}
value={input.value}
/>
{hasError ? <Text>{meta.error}</Text> : <Text />}
</Item>
);
}
MTTextInput.propTypes = {
input: PropTypes.shape({
onBlur: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
onFocus: PropTypes.func.isRequired,
value: PropTypes.any.isRequired
}).isRequired,
meta: PropTypes.shape({
active: PropTypes.bool.isRequired,
error: PropTypes.string,
invalid: PropTypes.bool.isRequired,
pristine: PropTypes.bool.isRequired,
visited: PropTypes.bool.isRequired
}).isRequired
};
Perhaps you may want to switch from an <Input/> component to a <TextInput/> component. Here is a generic example that you can find here:
import React from 'react';
import { TextInput, View, Text } from 'react-native';
/**
* to be wrapped with redux-form Field component
*/
export default function MyTextInput(props) {
const { input, meta, ...inputProps } = props;
const formStates = ['active', 'autofilled', 'asyncValidating', 'dirty', 'invalid', 'pristine',
'submitting', 'touched', 'valid', 'visited'];
return (
<View>
<TextInput
{...inputProps}
onChangeText={input.onChange}
onBlur={input.onBlur}
onFocus={input.onFocus}
value={input.value}
/>
<Text>The { input.name} input is:</Text>
{
formStates.filter((state) => meta[state]).map((state) => {
return <Text key={state}> - { state }</Text>;
})
}
</View>
);
}
This is my code for redux form. I have also set enableReinitialize to true and followed the react-form documentation.
I have hard coded the initialValues object just for testing purposes. But still my form is not getting initialized.
import React, { Component } from 'react';
import { Container, Header, Body, Content, Title, Button, Text, Left, Icon, Right } from 'native-base';
import { Field, reduxForm } from 'redux-form';
import { connect } from 'react-redux';
import MyTextInput from './TextInput';
import { fetchProfileData } from '../../actions';
const validate = values => {
const error = {};
error.email = '';
error.name = '';
error.mobile = '';
let ema = values.email;
let nm = values.name;
let mob = values.mobile;
if (values.email === undefined) {
ema = '';
}
if (values.name === undefined) {
nm = '';
}
if (values.mobile === undefined) {
mob = '';
}
if (ema.length < 8 && ema !== '') {
error.email = 'too short';
}
if (!ema.includes('#') && ema !== '') {
error.email = '# not included';
}
if (nm.length > 8) {
error.name = 'max 8 characters';
}
if (mob.length < 10 && mob !== '') {
error.mobile = 'min 10 digits';
}
return error;
};
class SimpleForm extends Component {
componentWillMount() {
this.props.fetchProfileData();
}
render() {
const { handleSubmit } = this.props;
console.log(this.props.initialValues);
return (
<Container>
<Header>
<Left>
<Button
transparent
onPress={() => this.props.navigation.navigate('DrawerOpen')}
>
<Icon name="menu" />
</Button>
</Left>
<Body>
<Title>Profile</Title>
</Body>
<Right />
</Header>
<Content padder>
<Field name='name' component={MyTextInput} label='Vendor Name' />
<Field name='company_name' component={MyTextInput} label='Company Name' />
<Field name='office_address' component={MyTextInput} label='Office Address' />
<Field name='email' component={MyTextInput} label='Email' />
<Field name='mobile' component={MyTextInput} label='Contact' />
<Button block primary onPress={handleSubmit((values) => console.log(values))} style={{ marginTop: 20 }}>
<Text>Save</Text>
</Button>
</Content>
</Container>
);
}
}
const mapStateToProps = state => {
return {
initialValues: { name: 'abcde#gmail.com' }
};
};
SimpleForm = connect(mapStateToProps, { fetchProfileData }
)(SimpleForm);
export default reduxForm({
form: 'test',
validate,
enableReinitialize: true
})(SimpleForm);
I've been struggling with this for some time. I've never been able to get anything with props to work. But I just need to some initial state stuff, so this works for me. Hope it works for you as well.
----- This works if you only want to set the initial values once.
export default reduxForm({
form: 'test',
validate,
initialValues: { name: 'abcde#gmail.com' }
})(SimpleForm);
Edit: It seemed I needed to pass variables in. This has been able to work for me for variables set in mapStateToProps. I think your setup didn't work because it is mapping state and props to the form, then adding reduxForm. It looks like it needs to be the other way around.
const mapStateToProps = (state) => {
return {
initialValues: {
name: 'foobar',
}
}
}
export default (connect(mapStateToProps, mapDispatchToProps)(reduxForm({
form: 'groupForm',
enableReinitialize: true
})(SimpleForm)))