Redux Form doesn't change values - javascript

Trying to make a form with Redux Form.
Here is my component
import React, { Component } from 'react'
import { Field, reduxForm } from 'redux-form'
import Form from './components/Form'
import TextFieldForReduxForm from './components/TextFieldForReduxForm'
import validate from './validate'
import { signUpCallbacks } from './onSubmit'
class SignUp extends Component {
render () {
return (
<Form
{...this.props}
formTitle='SIGN UP'
buttonTitle='SIGN UP'
linkTo='/sign/in'
linkTitle='Sign In'
>
<Field
component={TextFieldForReduxForm}
name='email'
label='Email'
margin='normal'
/>,
<Field
component={TextFieldForReduxForm}
name='password'
label='Password'
type='password'
margin='normal'
/>
</Form>
)
}
}
export default reduxForm({
form: 'signup',
validate,
onSubmit: signUpCallbacks.onSubmit,
onChange: values => console.log('onChange', values)
})(SignUp)
The problem is that on submit I always get required errors, which means the values somehow never change. And onChange is not called as well.
Here is validate.js
export const isValidEmail = email => /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(email)
const validate = values => {
console.log('validate', values)
const errors = {}
if (!values.email) {
errors.email = 'Required'
} else if (!isValidEmail(values.email)) {
errors.email = 'Invalid email address'
}
if (!values.password) {
errors.password = 'Required'
}
return errors
}
export default validate
And here is onSubmit.js
export const signUpCallbacks = {
onSubmit (values, dispatch, props) {
console.log(values)
}
}
Here are the components
Form.js
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { withStyles } from '#material-ui/core/styles'
import Grid from '#material-ui/core/Grid'
import Typography from '#material-ui/core/Typography'
import Button from '#material-ui/core/Button'
import { Link } from 'react-router'
const styles = {
button: {
margin: '15px 0px'
}
}
class Form extends Component {
static propTypes = {
classes: PropTypes.object,
formTitle: PropTypes.string,
buttonTitle: PropTypes.string,
linkTo: PropTypes.string,
linkTitle: PropTypes.string,
children: PropTypes.array,
handleSubmit: PropTypes.func,
submitting: PropTypes.bool
}
render () {
const {
classes,
formTitle,
buttonTitle,
linkTo,
linkTitle,
children,
submitting,
handleSubmit
} = this.props
return (
<form onSubmit={handleSubmit}>
<Grid
container
spacing={16}
alignItems='center'
direction='column'
justify='center'
>
<Typography variant='headline' gutterBottom>
{formTitle}
</Typography>
{children}
<Button
disabled={submitting}
type='submit'
variant='contained'
color='primary'
className={classes.button}
>
{buttonTitle}
</Button>
<Link to={linkTo}>{linkTitle}</Link>
</Grid>
</form>
)
}
}
export default withStyles(styles)(Form)
TextField
import React from 'react'
import PropTypes from 'prop-types'
import TextField from '#material-ui/core/TextField'
const TextFieldForReduxForm = props => {
const {
meta: {
touched,
error
},
label
} = props
const isErrored = error && touched
const displayErrorOrLabel = () => {
if (isErrored) return error
return label
}
return <TextField
{...props}
error={isErrored}
label={displayErrorOrLabel()}
/>
}
export default TextFieldForReduxForm
TextFieldForReduxForm.propTypes = {
meta: PropTypes.object,
label: PropTypes.string
}

The values, event handlers and other input props from redux-form's Field component are passed on to the form component inside props.input
Spread props.input instead of props inside the TextFieldForReduxForm component.

Related

React screen keyboard from class to function component

I am using the react-screen-keyboard library and there is the following code example:
import React, {Component, PropTypes} from 'react';
import Keyboard, {KeyboardButton} from 'react-screen-keyboard';
export default class Input extends Component {
static propTypes = {
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
onChange: PropTypes.func.isRequired,
onFocus: PropTypes.func,
}
static defaultProps = {
value: '',
onFocus: null,
}
state = {
inputNode: null,
}
handleInput = (event) => this.props.onChange(event.target.value)
handleFocus = () => {
if (this.props.onFocus) {
this.props.onFocus(this.input);
this.setState({inputNode: this.input});
// the `this.refs.input` value should be passed to the Keyboard component as inputNode prop
}
}
render() {
const {value} = this.props;
const {inputNode} = this.state;
return (
<div>
<input
onInput={this.handleInput}
value={value}
onFocus={this.handleFocus}
ref={(input) => { this.input = input; }}
/>
<Keyboard
inputNode={inputNode}
rightButtons={[
<ClickOnKeyPressWrap key="enter">
<KeyboardButton
onClick={this.handleLoginUser}
value="Войти"
classes="keyboard-submit-button"
/>
</ClickOnKeyPressWrap>
]}
/>
</div>
);
}
}
however, I haven't worked with React class components, and the PropTypes approach has long been deprecated. Tell me, how can I rewrite this example on a functional component?

getting material-ui TextField value onsubmit

I want to get the value of TextField input and render the message conditionally. I tried this one, its working but this one is functioning dynamically because I used onChange. I want to achieve the same but using onSubmit on <Button> Is there anyway to do that?
import React from 'react';
import { Component } from 'react';
import Button from '#mui/material/Button';
import { TextField } from '#mui/material';
class App extends Component
{
state = {
myValue: null,
}
handleChange = (e) => this.setState({
myValue: e.target.value
})
render() {
return (
<div>
<TextField
value={this.state.myValue}
onSubmit={this.handleChange}
/>
<button >Get Weather</button>
{this.state.myValue ? <p>value inputed </p>: <p>no input</p>}
</div>
)
}
}
export default App;
Using Refs is what you need. You can get the current value of your input by clicking a button and only then change the state.
Demo
import React, { createRef } from "react";
import { Component } from "react";
import { TextField } from "#mui/material";
class App extends Component {
constructor(props) {
super(props);
this.textInput = createRef();
this.state = {
myValue: ""
};
}
showRefContent = () => {
this.setState({
myValue: this.textInput.current.value
});
};
handleChange = (e) =>
this.setState({
myValue: e.target.value
});
render() {
return (
<div>
<TextField inputRef={this.textInput} />
<button onClick={this.showRefContent}>Get Weather</button>
<p>
{this.state.myValue.length > 0
? `text:${this.state.myValue}`
: "no text"}
</p>
</div>
);
}
}
export default App;
you just need to you onkeydown instead onsubmit.
<TextField
value={this.state.myValue}
onKeyDown={this.handleChange}
/>
or use
<form onSubmit={handleChange }>
<TextField
value={this.state.myValue}
onKeyDown={this.handleChange}
/>
<button type="submit">submit</button>
</form>

Argument of type is not assignable to parameter of type partial<>

I am trying validate my Ionic react form, but I getting the following error while calling the validationSchema in the useForm method. The error I am getting while trying to call the validationSchema in useForm is Argument of type '{ validationSchema: ...... is not assignable to parameter of type 'Partial<{ mode: "onBlur"...... Below is my code for the main login form:
import React, { useState } from "react";
import { IonPage, IonHeader, IonContent, IonToolbar, IonTitle, IonButton, IonAlert, IonItem, IonText, IonInput, IonLabel, IonRouterLink } from "#ionic/react";
import { RouteComponentProps } from 'react-router-dom';
import { LoginService } from "../services/LoginService";
import { LoginResonse } from 'common-models/APIResponses'
import { setIsLoggedIn, setIsAdmin, setEmailAddress } from "../data/user/user.actions";
import { connect } from '../data/connect';
import '../css/app.scss';
import { useForm } from 'react-hook-form';
import LoginInput, {LoginInputProps } from "../components/loginInput";
import { object, string } from 'yup';
interface OwnProps extends RouteComponentProps {}
interface DispatchProps {
setIsLoggedIn: typeof setIsLoggedIn;
setIsAdmin: typeof setIsAdmin;
setEmailAddress: typeof setEmailAddress;
}
interface LoginProps extends OwnProps, DispatchProps { }
const Login: React.FC<LoginProps>= ({setIsLoggedIn, setIsAdmin, history, setEmailAddress}) => {
const validationSchema = object().shape({
emailAddress: string().required().email(),
password: string().required()
});
// const [inputEmailAddress, setInputEmailAddress] = useState(''); <---- (1) Also, setting the state to '' doesn't lets me type anything in the input field.
// const [inputPassword, setInputPassword] = useState('');
const [showAlert, setShowAlert] = useState<boolean>(false);
const [canLogin, setCanLogin] = useState<boolean>(false);
const [formSubmitted, setFormSubmitted] = useState(false);
const { control, handleSubmit} = useForm({
validationSchema,
});
const formFields: LoginInputProps[] = [
{
name: "emailAddress",
component: <IonInput name="emailAddress" type="email" value="inputEmailAddress" spellCheck={false} autocapitalize="off" />,
label: "Email Address"
},
{
name: "password",
component: <IonInput name="password" type="password" value="inputPassword"/>,
label: "Password"
}
];
return (
<IonPage id="login-registration-page">
<IonHeader>
<IonToolbar color="primary">
<IonTitle>Login</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent>
<form className="login-registration-form ion-padding" onSubmit={handleSubmit(loginUser)}>
{formFields.map((field, index) => (
<LoginInput {...field} control={control} key={index} />
))}
<IonItem lines="none">
<IonRouterLink className="link" routerLink={'/forgot_username'}>Forgot Username?</IonRouterLink>
</IonItem>
<IonItem lines="none">
<IonRouterLink className="link" routerLink={'/forgot_password'}>Forgot Password?</IonRouterLink>
</IonItem>
<IonButton disabled={!canLogin} expand="block">Login</IonButton>
</form>
<IonAlert
isOpen={showAlert}
backdropDismiss={false}
onDidDismiss={() => setShowAlert(false)}
header={"EmailAddress or Password is incorrect."}
buttons={[
{
text: "OK",
}
]}
/>
</IonContent>
</IonPage>
);
};
export default connect<OwnProps, {}, DispatchProps>({
mapDispatchToProps: {
setIsLoggedIn,
setIsAdmin,
setEmailAddress
},
component: Login
})
I mostly focus on the middleware (API development) and in the backend. Not much in the front end side. Apologies if my question isn't framed right.
Also, I am unable to type anything in the input field if I set the state to '' Please refer to the comment starting with (1).
The interface which I use to set the loginProps (loginInput.ts file) is:
import React, { FC } from "react";
import { IonItem, IonInput, IonLabel} from "#ionic/react";
import { Controller, Control } from "react-hook-form";
export interface LoginInputProps {
name: string;
control?: Control;
label?: string;
component?: JSX.Element;
}
const LoginInput: FC<LoginInputProps> = ({
name,
control,
component,
label
})=>{
return (
<>
<IonItem>
{label && (
<IonLabel position="stacked">{label}</IonLabel>
)}
<Controller
as={component ?? <IonInput />}
name={name}
control={control}
onChangeName="onIonChange"
/>
</IonItem>
</>
);
};
export default LoginInput;
react-hook-form has been updated, you need to use schema resolvers now
https://react-hook-form.com/get-started#SchemaValidation
const { control, handleSubmit} = useForm({
resolver: yupResolver(validationSchema),
});

Redux form state is undefined

I'm using react-redux to access the state of a form from another component. This is how I export the form:
import _ from 'lodash';
import { reduxForm, Field } from 'redux-form';
import formFields from './formFields';
import OrderField from './OrderField';
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
class OrderForm extends Component {
constructor(props) {
super(props);
this.renderFields = this.renderFields.bind(this);
}
renderFields() {
return _.map(formFields, ({ label, name }) => {
return (
<Field
key={name}
component={OrderField}
type="text"
label={label}
name={name}
/>
);
});
}
render() {
return (
<div>
<form onSubmit={this.props.handleSubmit(this.props.onOrderSubmit)}>
{this.renderFields()}
<button type="submit" className="waves-effect waves-light btn-large red darken-2 white-text">
Submit Order
<i className="material-icons right">done</i>
</button>
<p>Pay cash on delivery</p>
</form>
</div>
);
}
}
export default reduxForm({
form: 'orderForm'
})(OrderForm);
Also a container form:
import OrderForm from './OrderForm';
import React, { Component } from 'react';
import { reduxForm } from 'redux-form';
class OrderNew extends Component {
constructor(props) {
super(props);
this.submitOrder = this.submitOrder.bind(this);
}
submitOrder(values) {
console.log("handle submit order");
}
render() {
return (
<OrderForm
onOrderSubmit={ this.submitOrder }/>
);
}
}
export default reduxForm({
form: 'orderForm'
})(OrderNew);
and I'm trying to access the state.form.orderForm.values property like so in another component:
function mapStateToProps(state) {
console.log(state);
return {
cart_items: state.order.cart_items,
products: state.order.products,
total: state.order.total,
formValues: state.form.orderForm.values
};
}
But I get the error that state.form.orderForm.values is undefined. What am I doing wrong?
In fact, console.log(state.form) prints {} in mapStateToProps.

Type is invalid -- expected a string

I am building a Meteor app with ReactJS and Semantic-UI for react. I have run into a problem when trying to create a form for a new item that appears as a modal. I receive the following error.
Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in. Check the render method of `App`.
App.jsx file:
import React, { Component, PropTypes } from 'react';
import { createContainer } from 'meteor/react-meteor-data';
import { Items } from '../api/items.js';
import { Item } from './Item.jsx';
import { ItemFormModal } from './ItemFormModal.jsx';
// App component - represents the whole app
export class App extends Component {
renderItems() {
return this.props.items.map((item) => (
<Item key={item._id} item={item} />
));
}
render() {
return (
<div className="container">
<header>
<h1>Products</h1>
<ItemFormModal />
</header>
<ul className="collection">
{this.renderItems()}
</ul>
</div>
);
}
}
App.propTypes = {
items: PropTypes.array.isRequired,
};
// creates container on client side for the collection
export default createContainer(() => {
return {
// fetch all the items available
items: Items.find({}, { sort: { createdAt: -1 } }).fetch(),
};
}, App);
EDIT: I have changed it to reflect the whole ItemFormModal.jsx:
import { Items } from '../api/items.js';
import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
// Import all semantic resources
import { Button, Icon, Header, Form, Modal } from 'semantic-ui-react';
export default class ItemFormModal extends React.Component {
constructor(props) {
super(props);
this.state = {
title: "",
price: 0.00,
modalOpen: false
}
this.handleOpen = this.handleOpen.bind(this);
this.handleClose = this.handleClose.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleOpen(event) { this.setState({ modalOpen: true }) }
handleClose(event) { this.setState({ modalOpen: false }) }
handleInputChange(event) {
const target = event.target;
const value = target.type === "checkbox" ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
handleSubmit(event) {
event.preventDefault();
let title = this.state.title.trim();
let price = this.state.price;
Items.insert({
title: title,
price: price,
recurring: false,
createdAt: new Date(), // current time
});
this.setState({
title: "",
price: 0.00
});
}
render() {
return (
<div id="new-item">
<Button onClick={this.handleOpen}>Create</Button>
<Modal
open={this.state.modalOpen}
onClose={this.handleClose}
size="small"
closeIcon="close">
<Modal.Header>Create new item</Modal.Header>
<Modal.Content>
<Form>
<Form.Group>
<Form.Input name="title" label="Title" placeholder="Title" value={this.state.title} onChange={this.handleInputChange}/>
<Form.Input name="price" label="Price" placeholder="Price" value={this.state.price} onChange={this.handleInputChange} />
</Form.Group>
</Form>
</Modal.Content>
<Modal.Actions>
<Button className="negative" onClick={this.handleClose}>Cancel</Button>
<Button className="positive" onClick={this.handleSubmit}>Create</Button>
</Modal.Actions>
</Modal>
</div>
)
}
}
Any help is greatly appreciated!
you are importing into App.jsx incorrectly. you have this:
import { ItemFormModal } from './ItemFormModal.jsx';
... which will not work if your export is marked as default. you can either remove "default" from your export, or you can change your import to this:
import ItemFormModal from './ItemFormModal.jsx';

Categories

Resources