React undefined this in function - javascript

someone can explain to me why this value in the renderInput function is undefined. I browse the code and everything looks good.
Here is error
Uncaught TypeError: Cannot read property 'renderError' of undefined
This is my component AddCatalog. When it calls console.log(this) in renderInput, this returns me undefinded
import React, {PropTypes} from "react";
import {Field, reduxForm} from "redux-form";
//
class AddCatalog extends React.Component {
constructor(props) {
super(props);
this.renderError = this.renderError.bind(this);
}
renderError({error, touched}) {
alert("asds");
if (touched && error) {
return <div className="red">{error}</div>;
}
}
renderInput({input, label, meta}) {
return (
<div className="form-group">
<label>{label}</label>
<input {...input} className="form-control" autoComplete="off" />
{this.renderError}
</div>
);
}
onSubmit(formValues) {
console.log(formValues);
}
render() {
return (
<form onSubmit={this.props.handleSubmit(this.onSubmit)}>
<div className="row paddingLR30 container-fluid">
<div className="col-12">
<h2>Dane placówki</h2>
</div>
<div className="col-3">
<Field label="Nazwa placówki*" name="name_kindergarten" component={this.renderInput} />
</div>
</div>
<button>Submit</button>
</form>
);
}
}
const validate = (formValues) => {
const errors = {};
if (!formValues.name_kindergarten) {
errors.name_kindergarten = "Musisz podać nazwę przedszkola";
}
return errors;
};
export default reduxForm({
form: "simple",
validate
})(AddCatalog);

Instead of calling this function like this.renderError() , you gave a pointer like this.renderError.
present code :
renderInput({input, label, meta}) {
return (
<div className="form-group">
<label>{label}</label>
<input {...input} className="form-control" autoComplete="off" />
{this.renderError}
</div>
);
}
correct code :
renderInput({input, label, meta}) {
return (
<div className="form-group">
<label>{label}</label>
<input {...input} className="form-control" autoComplete="off" />
{this.renderError()}
</div>
);
}

Because renderInput is not called in the context of the component - you forgot to bind it to this in the constructor the way you did with renderError.

Related

useState not working with render in React app

I am a beginner in React and trying to make a CRUD app. I have this code snipped where useState is being used but when I use the same in a class with render function it gives an error. Can someone explain me why and can convert this code to suit a class with render
export default function App(){
const[userName,setUserName]=useState('');
const[password,setPassword]=useState('');
return(
<div className="CreatePost">
<label>UserName</label>
<input type="text"
onChange={(e)=>{
setUserName(e.target.value);
}}
/>
<label>Password</label>
<input type="text"
onChange={(e)=>{
setPassword(e.target.value);
}}
/>
</div>
);
}
useState() only works in functional component. if you want to modify state in a class based component you have to do it with setState method. Something like this should work
export default class App extends React.Component{
constructor() {
this.state ={ userName : "" , password: "" }
}
render() {
return(
<div className="CreatePost">
<label>UserName</label>
<input type="text"
onChange={(e)=>{
this.setState( {userName : e.target.value} );
}}
/>
<label>Password</label>
<input type="text"
onChange={(e)=>{
this.setState( {password : e.target.value} );
}}
/>
</div>
);
}
}
You cannot use hook inside class component. Convert your existing component into the class based as following:
class App extends React.PureComponent {
state = {
userName: '',
password: '',
}
handleChange = (e) => {
this.setState({
[e.target.name]: e.target.value,
});
}
render () {
const { userName, password } = this.state;
return (
<div className="CreatePost">
<label>UserName</label>
<input type="text" value={userName} onChange={this.handleChange}/>
<label>Password</label>
<input type="text" value={password} onChange={this.handleChange}/>
</div>
)
}
}

How to call Promise.all in a handler function in React.js

I have two API calls that I am trying to make based on the input;
api.js
import axios from 'axios';
export const getWeather = input => {
};
export const getForecast = input => {
};
And in my React component:
import React, { Component } from 'react';
import WeatherDisplay from './WeatherDisplay';
import * as api from '../utils/api';
import dataTracker from '../utils/dataTracker';
import '../scss/app.scss';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
input: '',
weatherFromInput: null,
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
componentDidMount() {
console.dir(dataTracker);
}
handleChange(event) {
this.setState({
input: event.target.value,
});
}
// prettier-ignore
handleSubmit(event) {
event.preventDefault();
var promises = Promise.all(api.getWeather(this.state.input), api.getForecast(this.state.input))
promises.then(function(input) {
this.setState({ weatherFromInput: input[0], input: '' });
console.log("input", input[0]);
}.bind(this));
}
render() {
return (
<div className="container">
<div className="container">
<form name="weatherApp" onSubmit={this.handleSubmit}>
<h2>Open Weather App</h2>
<div className="row">
<div className="one-half column">
<label htmlFor="insertMode">Insert your location</label>
<input
name="zipcode"
className="u-full-width"
placeholder="please enter city or zipcode"
type="text"
autoComplete="off"
value={this.state.input}
onChange={this.handleChange}
/>
</div>
<div className="one-half column">
<label htmlFor="showMin">show minimum</label>
<input type="checkbox" />
<label htmlFor="showMax">show maximum</label>
<input type="checkbox" />
<label htmlFor="showMean">show mean</label>
<input type="checkbox" />
</div>
</div>
<div className="row">
<div className="two-half column">
<input type="submit" value="Submit" />
</div>
</div>
</form>
</div>
<div className="container">
<div className="row">
<div className="twelve columns">
{this.state.weatherFromInput !== null ? <WeatherDisplay weather={this.state.weatherFromInput} /> : null}
</div>
</div>
</div>
</div>
);
}
}
I get this error:
App.js:77 Uncaught (in promise) TypeError: undefined is not a function
Any help will be appreciated!
I believe Promise.all() needs an array, so:
var promises = Promise.all(api.getWeather(this.state.input), api.getForecast(this.state.input))
should be
var promises = Promise.all([api.getWeather(this.state.input), api.getForecast(this.state.input)])

Where does redux-form's `meta : { touched, error }` live? Could I access it when not contained by renderField scope?

The redux-form documentation advises me to render my input and submit-validation errors like this.
const renderField = ({ input, placeholder, className, type, meta: { touched, error } }) => (
<div>
<input {...input} className={className} placeholder={placeholder} type={type}/>
{touched && error && <span><font color="red">{error}</font></span>}
</div>
)
Inside render(){return(<form> </form>)} you are then supposed to create your inputs like this (note component={renderField} in the next code line):
<Field type="password" placeholder="password" className="form-control" component={renderField} name="password"/>
I wanted to customize this in order to fit it better into my own work. But I cannot seem to find a way to target touched and error unless I place the component in renderField, I guess I am still missing some vital knowledge. Where are these meta: {touched, error} properties going exactly and if I can access them somewhere?
Below is my entire container file for your reference.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Field, reduxForm } from "redux-form"
import Title from "../components/Title.js"
const renderField = ({ input, placeholder, className, type, meta: { touched, error } }) => (
<div className={"" + touched && error && "input_error_border"}>
<input {...input} className={className} placeholder={placeholder} type={type}/>
{touched && error && <span><font color="red">{error}</font></span>}
</div>
)
class RegisterForm extends React.Component {
constructor(props) {
super(props);
this.props = props;
}
is_registered(){
if(this.props.user.server && this.props.user.insert){
return (<div>
<p>Thank you for registering {this.props.user.first_name}</p>
<p>You will recieve an email with further instructions shortly.</p>
</div>)
}else{
return <div></div>
}
}
render() {
const { handleSubmit } = this.props
console.log(this.props)
return (
<form onSubmit={ handleSubmit } className="box-sizing mx-auto max_vertical_form_400">
<Title innerH="Register New User"/>
<div className="input-group-btn">
{this.is_registered()}
</div>
<div className="form-group">
<Field type="text" placeholder="first name" className="form-control" component={renderField} name="first_name" />
<Field type="text" placeholder="last name" className="form-control" component={renderField} name="last_name" />
</div>
<div className="form-group">
<Field type="text" placeholder="email" className="form-control" component={renderField} name="email"/>
</div>
<div className="form-group">
<Field type="text" placeholder="company" className="form-control" component={renderField} name="company"/>
<Field type="text" placeholder="department" className="form-control" component={renderField} name="department"/>
</div>
<div className="form-group">
<Field type="password" placeholder="password" className="form-control" component={renderField} name="password"/>
<Field type="password" placeholder="repeat password" className="form-control" component={renderField} name="password_repeated"/>
</div>
<div className="input-group-btn">
<button type="submit" className="btn btn-primary">Submit</button>
</div>
{/* <RegisterFormContainer />
<ThemeContainer /> */}
</form>
);
}
}
function validate(values){
const errors= {};
if(!values.password) errors.password = "missing password";
if (!values.email) {
errors.email = 'Required'
} else if (!/^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)) {
errors.email = 'Invalid email address'
}
return errors;
}
RegisterForm = reduxForm({
form: "register_user_form",
validate
})(RegisterForm)
function mapStateToProps({ user }) {
return { user };
}
export default RegisterForm = connect(mapStateToProps, null)(RegisterForm)
You can use redux-form selectors, specifically getFormMeta to know which fields are dirty or touched and getFormSyncErrors to know the fields having errors.
In your code, you need to change to import the selectors
import { getFormMeta, getFormSyncErrors } from 'redux-form';
add it to your mapStateToProps which might look like this:
function mapStateToProps(state) {
return {
user: state.user,
metaForm: getFormMeta('register_user_form')(state),
formSyncErrors: getFormSyncErrors('register_user_form')(state),
};
}
and then you can reference in your component with this.props.metaForm and this.props.formSyncErrors

Initialize my form with redux-form

I am facing trouble with initializing my form with redux-form and the 'initialValues' props. I read a lot posts (for instance here) and I don't manage to get it work...
I see that my initialValues is properly set but my fields are not updated with it... Maybe the issue is in my renderBasicField function but I don't know how to fix this.
Otherwise it could also be something like the prop is not populated yet when the component is rendered... But I don't know what to do to make it work as I must rely on mapStateToProps to feed it.
I saw a lot of post about this kind of issues and unfortunately for me I already set up the enableReinitialize property to true :)
Here is my code :
// My component
class ProfileForm extends React.Component {
render () {
console.log(this.props);
const { handleSubmit } = this.props;
const messageClassname = this.props.errorMessage !== undefined ? stylesShared.errorMessage : this.props.confirmationMessage !== undefined ? stylesShared.confirmationMessage : '';
return (
<div>
<div>
<div>
<form onSubmit={handleSubmit(this.props.onSubmitProfileUpdate)}>
<div>
<h4>Votre profil</h4>
</div>
<div className={messageClassname}>
{this.props.errorMessage &&
<span>{this.props.errorMessage}</span>
}
{this.props.confirmationMessage &&
<span>{this.props.confirmationMessage}</span>
}
</div>
<div>
<Field name='firstname' type='text' label='Prénom' component={renderBasicField} />
</div>
<div>
<Field name='lastname' type='text' label='Nom' component={renderBasicField} />
</div>
<div>
<Field name='email' type='email' label='Email' addon='#' component={renderBasicField} />
</div>
<div>
<Field name='telephone' type='text' label='Téléphone' component={renderBasicField} />
</div>
<div>
<Field name='ranking' className='input-row form-group form-control' options={this.getTennisRankingsOptions()} type='select' component={renderSelectField} />
</div>
<div>
<Field name='city' type='text' label='Ville' component={renderBasicField} />
</div>
<div>
<button className='btn btn-info btn-lg center-block' type='submit'>Mettre à jour</button>
</div>
</form>
</div>
</div>
</div>
);
}
}
const reduxFormDecorator = reduxForm({
form: 'profile',
enableReinitialize: true,
validate: validateProfileForm
});
const mapStateToProps = (state) => {
return {
initialValues: state.userConnection.loadProfile.user
};
};
const reduxConnector = connect(
mapStateToProps,
null
);
export default reduxConnector(reduxFormDecorator(ProfileForm));
And the code to render my field :
// My renderFunction
export const renderBasicField = ({input, meta: {touched, error}, label, type='text', id, addon, styleClasses, handleChange, controlledAsyncValue}) => {
const inputStyles = getInputStyles(input.value, touched, error);
if (controlledAsyncValue !== input.value) {
input.value = controlledAsyncValue;
input.onChange(input.value);
}
return (<div className={inputStyles.container}>
{displayInputLabel(inputStyles.input.idInput, label)}
<div className={addon && 'input-group'}>
{addon && <span className='input-group-addon'>{addon}</span>}
<input
{...input}
className={classNames(styles.basicInputField, styleClasses)}
id={id}
value={input.disabled ? '' : input.value}
onChange={getOnChangeAction(input.onChange, handleChange)}
placeholder={label}
type={type}
aria-describedby={inputStyles.input.ariaDescribedBy}
/>
</div>
{touched && error &&
displayErrorMessage(error)}
</div>);
};
I am wondering if I am ignoring the initialValue with my custom renderBasicField function but in that case, I would I retrieve this value to set my input ?
Thanks a lot for your help ! :)
Try to switch connect and form decorator. It should helps.
export default reduxFormDecorator(reduxConnector(ProfileForm));

Error while using react-router in multi step registration form

I get an error when i implement react router in my working multiple step registration form. When i click personal info link i get an error on console saying
"Uncaught TypeError: Cannot read property 'ownerName' of undefined"
"Uncaught TypeError: Cannot read property '_currentElement' of null".
Similiary when i click on Location i get same sort of errors, instead of ownerName i get error on "'city' of undefined". What might be the cause?
UPDATE: "react": "^0.14.7" and "react-router": "^2.0.0"
My Code
Index.js(entry point)
import React from 'react';
import {render} from 'react-dom';
import { Router, Route, IndexRoute, hashHistory } from 'react-router';
import assign from 'object-assign';
import Layout from './components/Layout';
let fieldValues = {
ownerName:'',
email:'',
phoneNumber:'',
city : '',
place : ''
}
class AddRent extends React.Component{
constructor(props,context) {
super(props,context);
this.state = {
step: 1
};
}
saveValues(field_value) {
return function() {
fieldValues = Object.assign({}, fieldValues, field_value)
}()
console.log('fieldValues are', fieldValues);
}
nextStep(step) {
var step = this.state.step;
var newStep = step+1;
this.setState({step:newStep});
}
previousStep(step) {
var step = this.state.step;
var newStep = step-1
this.setState({
step : newStep
});
}
showStep() {
switch (this.state.step) {
case 1:
return <RenderPersonalInfo fieldValues={fieldValues}
nextStep={this.nextStep.bind(this)}
previousStep={this.previousStep.bind(this)}
saveValues={this.saveValues.bind(this)} />
case 2:
return <RenderLocation fieldValues={fieldValues}
nextStep={this.nextStep.bind(this)}
previousStep={this.previousStep.bind(this)}
saveValues={this.saveValues.bind(this)} />
}
}
render() {
var style = {
width : (this.state.step / 2 * 100) + '%',
backgroundColor:'#000'
}
return (
<main>
<span className="progress-step">Step {this.state.step}</span>
<progress className="progress" style={style}></progress>
{this.showStep()}
</main>
)
}
}
class RenderPersonalInfo extends React.Component{
render(){
return(
<div>
<h3>Personal Information</h3>
<p className="subtitle">Provide your authentic information so rent seekers can contact you</p>
<hr/>
<div className="col-md-4">
<label htmlFor='name'>Owner Name</label>
<input ref="name" defaultValue={this.props.fieldValues.ownerName} type="textbox" className="form-control" id="name" placeholder="Owner name" />
</div>
<div className="col-md-4">
<label htmlFor="email">Email</label>
<input ref="email" defaultValue={this.props.fieldValues.email} type="email" className="form-control" id="email" placeholder="email" />
</div>
<div className="col-md-4">
<label htmlFor="phoneNumber">Phone Number</label>
<input ref="phone" defaultValue={this.props.fieldValues.phoneNumber} type="textbox" className="form-control" id="phoneNumber" placeholder="phone number" />
</div>
<hr/>
<div className="row continueBtn text-right">
<button className="btn how-it-works" ref="personalInfo" onClick={this.nextStep.bind(this)}>Continue</button>
</div>
</div>
);
}
nextStep(step){
var data = {
ownerName : this.refs.name.value,
email : this.refs.email.value,
phoneNumber: this.refs.phone.value,
}
console.log(data.ownerName);
if ((data.ownerName)&&(data.email)&&(data.phoneNumber)) {
this.props.saveValues(data);
this.props.nextStep();
}
else{
alert('please enter the name, email and phone number');
}
}
}
class RenderLocation extends React.Component{
render(){
return(
<div>
<h3>Help guests find your place</h3>
<p className="subtitle">will use this information to find a place that’s in the right spot.</p>
<hr/>
<div className="col-md-6">
<label htmlFor="city">City</label>
<input ref="city" defaultValue={this.props.fieldValues.city} type="textbox" className="form-control" id="city" placeholder="Biratnagar" />
</div>
<div className="col-md-6">
<label htmlFor="placeName">Name of Place</label>
<input ref="place" defaultValue={this.props.fieldValues.place} type="textbox" className="form-control" id="placeName" placeholder="Ganesh Chowk" />
</div><hr/>
<div className="row continueBtn">
<button className="btn how-it-works pull-left" onClick={this.props.previousStep.bind(this)}>Back</button>
<button className="btn how-it-works pull-right" onClick={this.nextStep.bind(this)}>Continue</button>
</div>
</div>
);
}
nextStep(step){
var data = {
city :this.refs.city.value,
place :this.refs.place.value,
}
if ((data.city)&&(data.place)) {
this.props.saveValues(data);
this.props.nextStep();
}
else{
alert('please enter the name of city and place');
}
}
}
render(
<Router history={hashHistory}>
<Route path="/" component={Layout}>
<Route path="personal" component={RenderPersonalInfo}></Route>
<Route path="location" component={RenderLocation}></Route>
</Route>
</Router>,document.getElementById('app')
);
Layout.js
import React from 'react';
import { Link } from 'react-router';
export default class Layout extends React.Component {
render() {
return (
<div className="container-fluid">
<h1>I am Layout</h1>
<div className="row">
<div className="col-sm-4">
<ul className="list-group">
<li className="list-group"><Link to="personal">Personal Info</Link></li>
<li className="list-group"><Link to="location">Location</Link></li>
</ul>
</div>
<div className="col-sm-8">
{this.props.children}
</div>
</div>
</div>
);
}
}
You are reading this.props.fieldValues.city and a few other values from this.props in RenderLocation that you never specified. The render() method crashes because this.props.fieldValues is undefined, and you get cryptic errors as the result.
Always look at the first errors (thrown by your code).
The subsequent errors like
"Uncaught TypeError: Cannot read property 'ownerName' of undefined"
"Uncaught TypeError: Cannot read property '_currentElement' of null".
are just symptoms of React getting confused after your code throws an earlier exception.
The AddRent component that used to specify these props is not used anywhere in your code. Since router uses the router configuration to create your components, it won’t give them any custom props—you need to read this.props.params supplied by the router and figure out the current step, as well as related fieldValues, from them.

Categories

Resources