I've written my code using semantic-ui-react and react redux, created a compose UI and when we pass inputs to the field it is showing error like this
"TypeError: Cannot read property 'value' of undefined"
I'm trying this using semantic-ui-react and react-redux
import React, { Component } from 'react'
import { Button, Header, Icon, Modal, Form} from 'semantic-ui-react';
import { connect } from "react-redux";
class Compose extends Component {
handleSubmit = (e) => {
e.preventDefault();
console.log("button triggered")
const From = this.getFrom.value;
const To = this.getTo.value;
const Subject = this.getSubject.value;
const outboxMessage = {
From,
To,
Subject
}
console.log(outboxMessage)
}
render() {
return (
<Modal trigger={<Button animated inverted color='blue'>
<Button.Content visible>Compose</Button.Content>
<Button.Content hidden>
<Icon name='plus'/>
</Button.Content>
</Button>} closeIcon>
<Modal.Content>
<Form onSubmit={this.handleSubmit}>
<Form.Input fluid label='From' type="text" ref={(input)=>this.getFrom = input} />
<Form.Input fluid label='To' type="text" ref={(input)=>this.getTo = input}/>
<Form.Input fluid label='Cc' type="text" ref={(input)=>this.getTo = input}/>
<Form.Input fluid label='Bcc' type="text" ref={(input)=>this.getTo = input}/>
<Form.Input fluid label='Subject' type='text'ref={(input)=>this.getSubject = input}/>
</Form>
<Button animated inverted color='blue' id='sendBtn'>
<Button.Content visible>Send</Button.Content>
<Button.Content hidden>
<Icon name='envelope'/>
</Button.Content>
</Button>
<Button animated inverted color='red'><Button.Content visible>Discard</Button.Content>
<Button.Content hidden>
<Icon name='remove circle'/>
</Button.Content>
</Button>
</Modal.Content>
</Modal>
);
}
}
export default Compose;
"In redux folder, reducer file:"
import actions from './actions';
const initialState = {
outboxMessage: []
}
const emailReducer = (state = initialState, action) => {
switch (action.type) {
case actions.OUTBOX_MESSAGE:
return {
...state,
outboxMessage: state.outboxMessage.concat([action.outboxMessage])
};
default:
return state;
}
}
export default emailReducer;
"In Index.js, mofidied in this way:"
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import emailReducer from './components/redux/reducer';
const store = createStore(emailReducer);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>, document.getElementById('root'));
I'm getting error Like this:
Compose.handleSubmit [as onSubmit]
F:/compose/src/components/Email/Compose.js:14
11 |
12 | handleSubmit = (e) => {
13 | e.preventDefault();
> 14 | const to = this.getTo.value;
| ^ 15 | const cc = this.getCc.value;
16 | const bcc = this.getBcc.value;
17 | const subject = this.getSubject.value;
You can make use of state, which is a cleaner way as well.
Maintain a state for all your input's.
state = {
from: "",
to: "",
cc: "",
bcc: "",
subject: ""
}
Now you can have Controlled Component.
We need name, value and onChange properties on input.
<Form onSubmit={this.handleSubmit}>
<Form.Input fluid label='From' type="text" name="from" value={this.state.from} onChange={this.changeHandler}/>
<Form.Input fluid label='To' type="text" name="to" value={this.state.to} onChange={this.changeHandler}/>
<Form.Input fluid label='Cc' type="text" name="cc" value={this.state.cc} onChange={this.changeHandler}/>
<Form.Input fluid label='Bcc' type="text" name="bcc" value={this.state.bcc} onChange={this.changeHandler}/>
<Form.Input fluid label='Subject' type='text' name="subject" value={this.state.subject} onChange={this.changeHandler}/>
</Form>
Now your change handler would be,
changeHandler = (e) => {
this.setState({
[e.target.name] : e.target.value
})
}
And finally you can use values from state,
handleSubmit = (e) => {
e.preventDefault();
console.log("button triggered")
//Get the values from state
const From = this.state.from;
const To = this.state.to;
const Subject = this.state.subject;
const outboxMessage = {
From,
To,
Subject
}
console.log(outboxMessage)
}
Related
I face a challenge in React about performance and how to handle a slow function.
Basically, I have a form with 6 inputs.
Each input component call the heavyCalculation() (slow function) function before the render function.
So when the user starts to type on the input field the render is very slow.
The question is how to handle this kind of situation without editing the heavyCalculation function?
I started to think about using the useMemo or useCallback hook but I am not pretty sure of myself.
App.js
import React, { useState } from "react";
import Input from "components/Input";
import Button from "components/Button";
import { Card } from "antd";
import "antd/dist/antd.css";
import * as S from "./style";
const App = () => {
const [form, setForm] = useState({ userName: "", email: "", password: "", firstName: "", lastName: "", city: "" });
const handleChange = (fieldName, value) => {
const tmpForm = { ...form };
tmpForm[fieldName] = value;
setForm(tmpForm);
};
const handleSubmit = () => {
console.log(form);
};
return (
<S.Content>
<Card title={"Sign Up"} bordered={false} style={{ width: 500, height: 450 }}>
<S.Form>
<Input value={form.userName} fieldName="userName" onChange={handleChange} placeholder="Username" />
<Input value={form.email} fieldName="email" onChange={handleChange} placeholder="Email" />
<Input value={form.password} fieldName="password" type="password" onChange={handleChange} placeholder="Password" />
<Input value={form.firstName} fieldName="firstName" onChange={handleChange} placeholder="First Name" />
<Input value={form.lastName} fieldName="lastName" onChange={handleChange} placeholder="Last Name" />
<Input value={form.city} fieldName="city" onChange={handleChange} placeholder="City" />
<Button onClick={handleSubmit} label="Submit" />
</S.Form>
</Card>
</S.Content>
);
};
export default App;
Input Component
import React from "react";
import { heavyCalculation } from "global";
import * as Antd from "antd";
const Input = ({ fieldName, value, onChange, placeholder }) => {
heavyCalculation();
const handleChange = (e) => {
onChange(fieldName, e.target.value);
};
return <Antd.Input value={value} onChange={handleChange} placeholder={placeholder} />;
};
export default Input;
heavyCalculation function
export const heavyCalculation = (num = 10) => {
for (let i = 0; i < num; i++) {
heavyCalculation(num - 1);
}
};
Thanks for the help
I'm trying to understand why dispatch is not available in my actions to no avail. Here is what I tried.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Field, Form } from 'react-final-form';
import { createProfile } from '../../actions/actions_members';
const onSubmit = async (values) => {
createProfile(values)
}
const Signup = () => (
<Form
onSubmit={onSubmit}
render={({ handleSubmit, submitting, pristine, values }) => (
<form onSubmit={handleSubmit} >
<label>Email:</label>
<Field type='text' className='input' component="input" type="text" name='email'/>
<label>Password:</label>
<Field className='input' component="input" type="password" name='password' />
{/*<label>Confirm password:</label>
<input type='password' className='input' name='password' {...password} />*/}
<button type="submit" disabled={submitting || pristine}>
Submit
</button>
</form>
)}
/>
)
export default connect()(Signup)
And here is my actions_members file
import * as C from './actions_const.js'
import { post, get } from '../helpers/apiConnection.js'
const createProfile = (value, dispatch) => {
var data = {
ep: "EP_SIGNUP",
payload : {
email: value.email,
password: value.password
}
}
post(data).then((result)=>dispatch({type:C.MEMBER_CREATE}));
}
export { createProfile }
I don't know how to pass dispatch to my createProfile action
You just have to pass it in from the onSubmit function.
const dispatch = useDispatch();
const onSubmit = async (values) => {
createProfile(values, dispatch)
}
The other option would be to import the store in your action_members file and use store.dispatch, which amounts to the same thing.
import * as C from './actions_const.js'
import { post, get } from '../helpers/apiConnection.js'
import store from '../whereverReduxStoreIsSetup.js';
const createProfile = (value) => {
var data = {
ep: "EP_SIGNUP",
payload : {
email: value.email,
password: value.password
}
}
post(data).then((result)=>store.dispatch({type:C.MEMBER_CREATE}));
}
export { createProfile }
I would like to change the state of a component based on the response of a PUT request using react-refetch.
Especially when the response of the PUT is unsuccessful, as is the case with for example a 500 response.
The following example is an example in a form. When a user submits the form it should then fire off a PUT.
If the PUT response is fulfilled, it should reset the form. Otherwise nothing should happen, and the user should be able to retry.
./MyForm.jsx
import React from "react";
import PropTypes from "prop-types";
import { PromiseState } from "react-refetch";
import { Formik, Form, Field, ErrorMessage } from "formik";
import ResetOnSuccess from "./ResetOnSuccess";
const MyForm = ({ settingsPut, settingsPutResponse }) => {
const submitForm = (values, formik) => {
settingsPut(true);
// Here it should pick up the settingsPutResponse,
// and then do the following ONLY if it's successful:
//
// formik.resetForm({ values });
// window.scrollTo(0, 0);
};
return (
<div>
<Formik
noValidate
initialValues={{ name: "", password: "" }}
onSubmit={submitForm}
>
{({ dirty }) => (
<Form>
<ResetOnSuccess settingsPutResponse={settingsPutResponse} />
<Field type="text" name="name" />
<ErrorMessage name="name" component="div" />
<Field type="password" name="password" />
<ErrorMessage name="password" component="div" />
<button type="submit" disabled={dirty !== null ? !dirty : false}>
Submit
</button>
{settingsPutResponse && settingsPutResponse.rejected && (
<p style={{ color: "red" }}>Please try again</p>
)}
</Form>
)}
</Formik>
</div>
);
};
MyForm.propTypes = {
settingsPut: PropTypes.func.isRequired,
settingsPutResponse: PropTypes.instanceOf(PromiseState)
};
MyForm.defaultProps = {
userSettingsPutResponse: null
};
export default MyForm;
I might have a solution by creating a component:
./ResetOnSuccess.jsx
import React, { useEffect, useState } from "react";
import { useFormikContext } from "formik";
import PropTypes from "prop-types";
import { PromiseState } from "react-refetch";
const ResetOnSuccess = ({ settingsPutResponse }) => {
const { values, resetForm } = useFormikContext();
const [success, setSuccess] = useState(false);
useEffect(() => {
if (settingsPutResponse && settingsPutResponse.fulfilled) {
setSuccess(true);
}
}, [settingsPutResponse]);
// only if settingsPutResponse is fulfilled will it reset the form
if (success) {
resetForm({ values });
window.scrollTo(0, 0);
setSuccess(false);
}
return null;
};
ResetOnSuccess.propTypes = { settingsPutResponse: PropTypes.instanceOf(PromiseState) };
ResetOnSuccess.defaultProps = { settingsPutResponse: null };
export default ResetOnSuccess;
And then in ./MyForm.jsx add the reset component:
<Formik
noValidate
initialValues={{ name: "", password: "" }}
onSubmit={submitForm}
>
{({ dirty }) => (
<Form>
<ResetOnSuccess settingsPutResponse={settingsPutResponse} />
<Field type="text" name="name" />
<ErrorMessage name="name" component="div" />
<ResetOnSuccess settingsPutResponse={settingsPutResponse} />
// etc...
But since it's a component that returns a 'null'. This feels a bit like an anti-pattern.
Is there a better way?
I've created an codesandbox example here: https://codesandbox.io/s/quizzical-johnson-dberw
I am using react-final-form to create Registration and Login Forms. I have created file called validators with 3 functions: required, allowedEmails, and validatePassword length. Then I used them in my fields with validate={required} or validate={this.composeValidators(required, validatePasswordLength)}, if I wanted to use more than two validators for my fields.
I have 5 fields: FirstName, LastName, Username, Email and password. At the beginning when the fields are empty everything works well. All of them show error:
"This field is required"
but when I try to enter some value in the specific field, the error for that field still remains.
Here is sandbox link to my project https://codesandbox.io/s/forma-ktvjq
This are functions in my validators file:
export function validateEmail(email) {
var re = /^(([^<>()[\]\\.,;:\s#"]+(\.[^<>()[\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(String(email).toLowerCase());
}
export const required = v => {
if(!v || v === ' '){
return 'This field is requireddd';
}
return undefined;
}
export const allowedEmails = v =>{
if (v === validateEmail) {
return "email is not valid";
}
return undefined;
};
export const validatePasswordLength = v => {
if(v){
if(v.length<8){
return "Password has to be atleast 8 charachters";
}
return "Password accepted";
}
import validators, { required, validatePasswordLength, allowedEmails, validateEmail } from '../../validators';
import LoginForm from '../LoginForm';
// import Container from 'react-bootstrap/Container';
// import Row from 'react-bootstrap/Row';
// import Col from 'react-bootstrap/Col';
import React, { Component } from 'react';
import { Form, Field } from 'react-final-form';
import { InputPassword, InputEmail, InputUsername, InputFirstName, InputLastName, InputField} from '../.././../components/InputFields';
import Button from 'react-bootstrap/Button'
import { metaProperty } from '#babel/types';
import { withRouter } from 'react-router-dom';
class RegisterForm extends Component {
sleep = ms => new Promise(res => setTimeout(res, ms));
newPage = (newPage) => {
this.props.history.push('/login');
};
handleSubmit = async (values,e) => {
e.preventDefault()
await this.sleep(300);
console.log("values", values);
this.newPage();
};
composeValidators = (...validators) => value => validators.reduce((error, validator) => error || validator(value), undefined);
render(){
return (
<div>
<h1>đ Register Form</h1>
<br>
</br>
<br></br>
<Form
onSubmit={this.handleSubmit.bind(this)}
render={ ( { handleSubmit, values, submitting, meta }) => (
<form onSubmit={handleSubmit}>
<Field
name="firstName"
component={InputField}
hintText="First Name"
floatingLabelText="First Name"
validate={required}
type="text"
/>
<Field
name="lastName"
component={InputField}
hintText="Last Name"
floatingLabelText="Last Name"
validate={required}
type="text"
/>
<Field
name="username"
component={InputField}
hintText="UserName"
floatingLabelText="username"
validate={required}
type="text"
/>
<Field
name="password"
component={InputField}
hintText="Password"
floatingLabelText="Password"
validate={this.composeValidators(required, validatePasswordLength)}
type="password"
/>
<Field
name="email"
component={InputField}
hintText="email"
floatingLabelText="Email"
validate={this.composeValidators(required, allowedEmails)}
type="email"
/>
<Button size="lg" type="submit">Register</Button>
</form>
) } />
</div>
);
};
}
export default RegisterForm;
Here is also my third party component that i am using in Field as component.
import { Form, Field } from 'react-final-form';
import LoginForm from '../containers/forms/LoginForm';
import RegisterForm from '../containers/forms/RegisterForm';
import './InputFields.css'
export const InputField = (props) => {
console.log("PROOOPS ", props);
return(
<div>
<InputGroup size="lg" className="mb-4">
<FormControl
placeholder={props.floatingLabelText}
type={props.input.type}
/>
</InputGroup>
<div className="ValidatorStyle" >
{props.meta.error && props.meta.touched && <span>{props.meta.error}</span>}
</div>
</div>
)}
I don't get any error, but the field value does not get updated.
In InputField.js you need to spread the input props on InputGroup, like:
<InputGroup size="lg" className="mb-4" {...props.input}>
(That includesvalue, onChange and more)
I have a contact page on which I have a contact form defined like this:
import React from "react";
import { Field, reduxForm } from "redux-form";
import Recaptcha from "react-recaptcha";
const required = value => (value ? undefined : "This field is required.");
const email = value => value && !/^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value) ? "Invalid email address." : undefined;
const renderInput = ({
input,
label,
type,
meta: { touched, error }
}) => (
<div className="form-group">
<label className="col-sm-2 control-label">{ label }</label>
<div className="col-sm-10">
{ (type == "text" || type == "email") ? <input { ...input } type={ type } /> : <textarea { ...input }></textarea> }
{ touched && ((error && <span className="contact-form-error-message">{ error }</span>)) }
</div>
</div>
);
const captcha = (props) => (
<div className="form-group">
<label className="col-sm-2 control-label"></label>
<div className="col-sm-10">
<Recaptcha render="explicit" onloadCallback={ console.log.bind(this, "reCAPTCHA loaded.") }
sitekey="XXXXXXXXXXXXXXXXX" onChange={props.input.onChange} />
</div>
</div>
);
const ContactForm = props => {
const { handleSubmit, submitting } = props
return (
<form className="form-horizontal" onSubmit={ handleSubmit }>
<Field
name="name"
type="text"
component={ renderInput }
label="Name:"
validate={ required }
/>
<Field
name="email"
type="email"
component={ renderInput }
label="Email:"
validate={ [required, email] }
/>
<Field
name="subject"
type="text"
component={ renderInput }
label="Subject:"
validate={ required }
/>
<Field
name="message"
type="textarea"
component={ renderInput }
label="Message:"
validate={ required }
/>
<Field name="recaptchacode" component={ captcha } />
<div className="form-group">
<label className="col-sm-2 control-label"></label>
<div className="col-sm-10">
<button type="submit" id="contact-form-button" disabled={ submitting }>Send</button>
</div>
</div>
</form>
)
}
export default reduxForm({
form: "ContactForm"
})(ContactForm);
The problem is I cannot seem to get the recaptchacode field in the values object when I click the submit button. How do I bind the value of the Recaptcha component to redux-form so that it puts it in the values object?
And since StackOverflow wants me to add more explanation to this because there's too much code, I am writing this text.
So the answer in short as I have managed to get this thing working. There are two npm packages for implementing recaptcha in react:
react-recaptcha and react-google-recaptcha. You want the second one and not the first one (which was my problem and doesn't work with redux-form) and then you want to follow this tutorial: https://github.com/erikras/redux-form/issues/1880
Hope this helps someone.
Hereâs how I integrated Google ReCaptcha with React and redux-forms with Language support. Hope this will help someone.
Versions:
React: 16.5.2
react-google-recaptcha: 1.0.5
react-redux: 5.0.6
redux: 3.7.2
redux-form: 7.2.0
Redux form:
import React from 'react';
import {
reduxForm,
Field,
formValueSelector,
change,
} from 'redux-form';
import { testAction } from â/actions';
import { connect } from 'react-redux';
import Captcha from './Captcha';
const validate = values => {
const errors = {};
if (!values.captchaResponse) {
errors.captchaResponse = 'Please validate the captcha.';
}
return errors;
};
let TestForm = (props) => {
const {
handleSubmit,
testAction,
language, //extract language from props/or hard code it in Captcha component
} = props;
return (
<Form onSubmit={ handleSubmit(testAction)}>
<Field component={Input} name=âother_input_nameâ type="text" required/>
<Field dispatch={props.dispatch} component={Captcha} change={change} language={language} name="captchaResponse"/> {/* Pass redux-forms change and language to the Captcha component*/}
<Button type="submit">{âValidateâ}</Button>
</Form>
);
};
const selector = formValueSelector('testForm');
TestForm = connect(
state => ({
recaptchaValue: selector(state, 'captchaResponse'),
}),
{ testAction: testAction }
)(TestForm);
export default reduxForm({
form: âtestFormâ,
validate,
enableReinitialize: true,
})(TestForm);
Captcha component:
import React, { Component } from 'react';
import ReCAPTCHA from 'react-google-recaptcha';
import styled from 'styled-components';
import { change } from 'redux-form';
class Captcha extends Component {
constructor(props) {
super(props);
window.recaptchaOptions = { lang: this.props.language }; //set language that comes from props E.g.: fr/es/en etc..
}
onChange = (value) => {
this.props.meta.dispatch(change(âtestFormâ, 'captchaResponse', value));
};
render() {
const { meta: { touched, error } } = this.props;
return (
<CaptchaWrapper>
<ReCAPTCHA
sitekey={âxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxâ}
onChange={response => this.onChange(response)}
/>
<ErrorMessage>{ touched ? error : '' }</ErrorMessage>
</CaptchaWrapper>
);
}
}
const CaptchaWrapper = styled.div`
`;
const ErrorMessage = styled.p`
color: red;
`;
export default Captcha;