Function is undefined in react - javascript

I have a function named Addphoto and I am passing that by props but I am getting an error that Addphoto is not a function
this is my main.js where i created routes
<div>
<Routes>
<Route exact path="/" element={<><Title todo={'PhotoFrame'} /> <PhotoFrame PhotoBlock={this.state.allPhotos} onRemovePhoto={this.removePhoto} /></>}/>
<Route path="/AddPhoto" element={<AddPhoto onAddPhoto={(addedPhoto)=>{
console.log(addedPhoto)
}}/>} />
</Routes>
</div>
here is the code of addphoto.js where i creatd ny function
class AddPhoto extends Component{
constructor(){
super()
this.handlesSubmit = this.handlesSubmit.bind(this)
}
handlesSubmit(event){
event.preventDefault();
const imageLink = event.target.elements.link.value
const description = event.target.elements.description.value
const photo = {
id: 0,
description:description,
imageLink:imageLink
}
console.log(photo)
if(description&&imageLink){
// this.props.onAddphoto(photo)
this.props.AddPhoto()
}
}
render(){
return(
<div>
<h1>photo frame</h1>
<div className='form'>
<form onSubmit={this.handlesSubmit}>
<input type="text" placeholder='Link' name='link'/>
<input type="text" placeholder='Description' name='description'/>
<button>Submit</button>
</form>
</div>
</div>
)
}
}
here is the error is occuring in my console

So, you gave a different name to the prop -onAddPhoto.
If you want to have access to addPhoto function inside AddPhoto.js:
const addPhoto = () => {
// functionality to add a photo
}
<Route path="/AddPhoto" element={<AddPhoto addPhoto={addPhoto}/>
Now you can call this.props.addPhoto() inside AddPhoto component.

Related

Conditional routing with ReactRouter in React

class Login extends Component {
async handle_login(e) {
e.preventDefault();
this.props.history.push('/home')
}
render() {
return (
<input type='submit' value='Log in' onSubmit={(e)=>this.handle_login(e)}></input>
<input type='submit'value='Sign up' onSubmit={(e)=>this.handle_signup(e)}></input>
);
}
}
export default withRouter(Login)
class App extends Component {
// app.js
render() {
return (
<Router>
<div className='App' style={{marginTop:'0px'}}>
<div className='container'>
<Header></Header>
<Route exact style={{marginTop:'0px'}} path='/' render={(props)=>(
<div style={{display:'flex',justifyContent:'center'}}>
{/* add a background in this div */}
<Link to='/login' style={{color:'#000000', fontSize:'large',paddingRight:'10px' }}> Login </Link>
<Link to='/' style={{color:'#000000', fontSize:'large' }}> Index </Link>
</div>
)}></Route>
<Route exact path='/home' component={Home}></Route>
<Route exact path='/login' component={Login}></Route>
</div>
</div>
</Router>
);
}}
export default App;
I am trying to redirect the 'login' component to the '/home' using withRouter using the aforementioned code, but running the code does nothing, neither does it throw any error.I have attached the codes of both the home and the login components.
The main issue is probably because you forgot your constructor to get the props and bind your method.
Update your class to this
class Login extends Component {
constructor(props) {
super(props);
// This binding is necessary to make `this` work in the callback
this.handle_login = this.handle_login.bind(this);
}
// No "async" need here
handle_login(e) {
e.preventDefault();
this.props.history.push('/home')
}
render() {
return (
<input type='submit' value='Log in' onSubmit={(e)=>this.handle_login(e)}></input>
<input type='submit'value='Sign up' onSubmit={(e)=>this.handle_signup(e)}></input>
);
}
}
export default withRouter(Login)
I would also suggest passing your method to the onSubmit handle, instead of creating a new function there:
<input type='submit' value='Log in' onSubmit={this.handle_login}></input>
Update
I also notice that you have 2 inputs of type submit, which is not very common. Your action is also in the onSubmit and not onClick, but you don't have a <form> which is usually what triggers the submit function.
My suggestion is to review your HTML structure as well and make sure it make sense. For now, try this to at least get your method working:
render() {
// buttons have type="submit" by default, so no need to include that
return (
<button value='Log in' onClick={(e)=>this.handle_login(e)}></input>
);
}
There is an interesting discussion here, as additional reference.
#BrunoMonteiro is correct but there is an alternate option for this you can declare your function as arrow function so that you don't have to bind
class Login extends Component {
constructor(props) {
super(props);
}
handle_login=async(e)=> {
e.preventDefault();
this.props.history.push('/home')
}
render() {
return (
<input type='submit' value='Log in' onClick={(e)=>this.handle_login(e)}></input>
<input type='submit'value='Sign up' onClick={(e)=>this.handle_signup(e)}></input>
);
}
}
export default withRouter(Login)
also make sure you have access to history property in your props for checking this you can do console.log(this.props) and check whether it has required property or not

React Failed prop type: value without onChange handler

This is my form component:
Form.jsx
import React, { Component } from 'react';
import axios from 'axios';
import { Redirect } from 'react-router-dom';
class Form extends Component {
constructor (props) {
super(props);
this.state = {
formData: {
restaurant: '',
username: '',
email: '',
password: ''
}
};
this.handleUserFormSubmit = this.handleUserFormSubmit.bind(this);
this.handleFormChange = this.handleFormChange.bind(this);
};
componentDidMount() {
this.clearForm();
};
componentWillReceiveProps(nextProps) {
if (this.props.formType !== nextProps.formType) {
this.clearForm();
};
};
clearForm() {
this.setState({
formData: {restaurant: '', username: '', email: '', password: ''}
});
};
handleFormChange(event) {
const obj = this.state.formData;
obj[event.target.name] = event.target.value;
this.setState(obj);
};
handleUserFormSubmit(event) {
event.preventDefault();
const formType = this.props.formType
const data = {
restaurant: this.state.formData.restaurant,
email: this.state.formData.email,
password: this.state.formData.password
};
if (formType === 'register') {
data.username = this.state.formData.username
};
const url = `${process.env.REACT_APP_WEB_SERVICE_URL}/auth/${formType}`;
axios.post(url, data)
.then((res) => {
this.clearForm();
this.props.loginUser(res.data.auth_token);
})
.catch((err) => { console.log(err); });
};
render() {
if (this.props.isAuthenticated) {
return <Redirect to='/' />;
};
return (
<div>
{this.props.formType === 'Login' &&
<h1 className="title is-1">Log In</h1>
}
{this.props.formType === 'Register' &&
<h1 className="title is-1">Register</h1>
}
<hr/><br/>
<form onSubmit={(event) => this.handleUserFormSubmit(event)}>
{this.props.formType === 'Register' &&
<div className="field">
<input
name="restaurant"
className="input is-medium"
type="text"
placeholder="Enter your restaurant name"
required
value={this.state.formData.restaurant}
onChange={this.props.handleFormChange}
/>
</div>
}
<div className="field">
<input
name="username"
className="input is-medium"
type="text"
placeholder="Enter a username"
required
value={this.state.formData.username}
onChange={this.props.handleFormChange}
/>
</div>
<div className="field">
<input
name="email"
className="input is-medium"
type="email"
placeholder="Enter an email address"
required
value={this.state.formData.email}
onChange={this.props.handleFormChange}
/>
</div>
<div className="field">
<input
name="password"
className="input is-medium"
type="password"
placeholder="Enter a password"
required
value={this.state.formData.password}
onChange={this.props.handleFormChange}
/>
</div>
<input
type="submit"
className="button is-primary is-medium is-fullwidth"
value="Submit"
/>
</form>
</div>
)
};
};
export default Form;
and this is my app component:
App.jsx
import React, { Component } from 'react';
import { Route, Switch } from 'react-router-dom';
import axios from 'axios';
import UsersList from './components/UsersList';
import About from './components/About';
import NavBar from './components/NavBar';
import Form from './components/Form';
import Logout from './components/Logout';
import UserStatus from './components/UserStatus';
class App extends Component {
constructor() {
super();
this.state = {
users: [],
title: 'Test.io',
isAuthenticated: false,
};
this.logoutUser = this.logoutUser.bind(this);
this.loginUser = this.loginUser.bind(this);
};
componentWillMount() {
if (window.localStorage.getItem('authToken')) {
this.setState({ isAuthenticated: true });
};
};
componentDidMount() {
this.getUsers();
};
getUsers() {
axios.get(`${process.env.REACT_APP_WEB_SERVICE_URL}/users`)
.then((res) => { this.setState({ users: res.data.data.users }); })
.catch((err) => { });
};
logoutUser() {
window.localStorage.clear();
this.setState({ isAuthenticated: false });
};
loginUser(token) {
window.localStorage.setItem('authToken', token);
this.setState({ isAuthenticated: true });
this.getUsers();
};
render() {
return (
<div>
<NavBar
title={this.state.title}
isAuthenticated={this.state.isAuthenticated}
/>
<section className="section">
<div className="container">
<div className="columns">
<div className="column is-half">
<br/>
<Switch>
<Route exact path='/' render={() => (
<UsersList
users={this.state.users}
/>
)} />
<Route exact path='/about' component={About}/>
<Route exact path='/register' render={() => (
<Form
formType={'Register'}
isAuthenticated={this.state.isAuthenticated}
loginUser={this.loginUser}
/>
)} />
<Route exact path='/login' render={() => (
<Form
formType={'Login'}
isAuthenticated={this.state.isAuthenticated}
loginUser={this.loginUser}
/>
)} />
<Route exact path='/logout' render={() => (
<Logout
logoutUser={this.logoutUser}
isAuthenticated={this.state.isAuthenticated}
/>
)} />
<Route exact path='/status' render={() => (
<UserStatus
isAuthenticated={this.state.isAuthenticated}
/>
)} />
</Switch>
</div>
</div>
</div>
</section>
</div>
)
}
};
export default App;
This is the error console is showing:
index.js:1446 Warning: Failed prop type: You provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultValue`. Otherwise, set either `onChange` or `readOnly`.
in input (at Form.jsx:72)
in div (at Form.jsx:71)
in form (at Form.jsx:69)
in div (at Form.jsx:61)
in Form (at App.jsx:66)
in Route (at App.jsx:65)
in Switch (at App.jsx:58)
in div (at App.jsx:56)
in div (at App.jsx:55)
in div (at App.jsx:54)
in section (at App.jsx:53)
in div (at App.jsx:48)
in App (at src/index.js:9)
in Router (created by BrowserRouter)
in BrowserRouter (at src/index.js:8)
I don't get it, though, because form change is being handled at <input> in the code above, like so:
onChange={this.props.handleFormChange}
so what am I missing? forms are not even accepting inputs.
You have fundamentally misunderstood the props concept in React components. I will try to explain it with a more simplified version of your app. Lets take the form example.
class Form extends Component {
handleFormChange(){
console.log("This is the form change function inside -Form-");
}
render(){
return(
<div>
<input
name="email"
type="text"
value={this.state.email}
onChange={this.handleFormChange} // Focus point 1 - Calls local function
/>
<input
name="username"
type="text"
value={this.state.username}
onChange={this.props.handleFormChange} // Focus point 2 - Calls function passed down via props
/>
</div>
);
}
}
class App extends Component {
handleFormChange(){
console.log("This is the form change function inside -App-");
}
render(){
return <Form handleFormChange={this.handleFormChange} />
}
}
As you can see the App is going to render the Form component. Look at Focus point 1 and 2. In the first focus point its trying to access the local 'handleFormChange' function. And the 2nd one tries to call whatever the function that is provided by the parent via props.
So what happened is that you are telling the 'Form' component to access the handleFormChange function which should have been provided by the parent as a "prop" i.e this.props.handleFormChange. So when the component is mounted React tries to bind this.props.handleFormChange to the onChange event of the input.
But in your instance, the 'handleFormChange' prop in the component is not provided. hence this.props.handleFormChange will be undefined resulting in that warning.
So to wire up any handlers that are within the Form component they should not be linked with 'this.props'. Whatever handlers that are accessed via props should be provided by the parent when initializing the component.
Its because you are not passing any prop named as handleFormChange from App.jsx to the Form component.
Instead, it's in your own Form component.
So, just try this onChange={this.handleFormChange}

how to Stop rerendering of entire component onChange event on input text field in reactJs

I m new to reactJs and i m creating user Authentication functionality. I have two components one is header which has navbar and it contains react-router routers and the other is login component which has two input fields ... The problem with login component is when i start typing in input field it loses focus after each character typed i know it is rerendering the whole component but i don't know how to solve this problem
header.js
changeName = (e) => {
this.setState({name : e.target.value})
}
changePass = (e) => {
this.setState({password:e.target.value})
}
login = () => {
var name = this.state.name;
var password = this.state.password
var mysession;
$.ajax({
url : 'http://localhost:4000/login',
type : "POST",
data : {username:name,password:password},
success : function(data){
if(data == true){
this.setState({sessionFlag:true})
$('#home')[0].click();
}
else {
this.setState({sessionFlag:false})
}
}.bind(this)
})
}
render(){
const {name,password} = this.state;
return (
<Router>
<div>
<Route path="/login" exact component={()=><Login
onClickHandle={this.login.bind(this)}
onChangeName={this.changeName.bind(this)}
onChangePass={this.changePass.bind(this)}
name={name}
password = {password} />} />
</div>
</Router>
)
}
login.js
render(){
return (
<form className="form-horizontal" method ="post">
<input
type="text"
onChange={this.props.onChangeName}
value={this.props.name}/>
<input type="text"
onChange={this.props.onChangePass}
value={this.props.password} />
<input type="button"
value="Login"
onClick={this.props.onClickHandle} />
</form>
)
}
The main issue is the manner in which you are specifying your Login component:
<Route
path="/login"
exact
component={() => (
<Login
onChangeName={this.changeName.bind(this)}
onChangePass={this.changePass.bind(this)}
name={this.state.name}
password={this.state.password}
/>
)}
/>
Using this syntax causes the child of the Route to look like a brand-new type of component with each rendering (since it will be a new arrow function instance each time) so the previous Login component will be completely unmounted and the new one mounted.
From https://reactrouter.com/web/api/Route/component:
When you use component (instead of render or children, below) the router uses React.createElement to create a new React element from the given component. That means if you provide an inline function to the component prop, you would create a new component every render. This results in the existing component unmounting and the new component mounting instead of just updating the existing component. When using an inline function for inline rendering, use the render or the children prop (below).
Here is an example using the render-func approach:
Header.js
import React from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import Login from "./Login";
class Header extends React.Component {
constructor(props) {
super(props);
this.state = { name: "", password: "" };
this.changeName = this.changeName.bind(this);
this.changePass = this.changePass.bind(this);
}
changeName = (e) => {
this.setState({ name: e.target.value });
};
changePass = (e) => {
this.setState({ password: e.target.value });
};
render() {
return (
<Router>
<div>
<div>
<Link to="/login">Login</Link>
</div>
<Route
path="/login"
exact
render={() => (
<Login
onChangeName={this.changeName}
onChangePass={this.changePass}
name={this.state.name}
password={this.state.password}
/>
)}
/>
</div>
</Router>
);
}
}
export default Header;

React-Router - Pass Method to Child Component

I have a button nested within a component called "Create" that has to trigger a change in state that changes the state in app.js and renders a fresh view.
I can't seem to pass the method changeHPage from app.js to the Create component. I am using React-Router and normally I would simply write <App changeHPage={this.changePage}> to pass the method to its child component and call it using this.props.changeHpage but I can't pass props via this method when using React Router.
Any help on how to pass a method to a child component using React Router would be much appreciated. My code can be found below.
app.js:
/* STRICT MODE: See `../../server.js` */
'use strict';
/* GLOBAL REACT REQUIRES */
// React.js
const React = require('react');
// React-DOM for HTML rendering
const ReactDOM = require('react-dom');
// React router for dynamic pathing. Has several component features that need to be required to use.
const ReactRouter = require('react-router');
// 4 components pulled from ReactRouter:
const Router = ReactRouter.Router;
const Route = ReactRouter.Route;
const Navigation = ReactRouter.Navigation;
const Link = ReactRouter.Link;
const browserHistory = ReactRouter.browserHistory;
/* Relative paths to external components */
const auth = require('./helpers/auth.js');
const requireAuth = require('./helpers/requireauth.js');
const About = require('./components/about.js');
const Login = require('./components/login.js');
const Logout = require('./components/logout.js');
const Signup = require('./components/signup.js');
const Header = require('./components/header.js');
const Create = require('./components/create.js');
const NotFound = require('./components/notfound.js');
const Veri = require('./components/veri.js');
/* React App Creation */
const App = React.createClass({
// Declares the initial state when app is loaded
getInitialState : function() {
return {
loggedIn: auth.loggedIn(),
change: true,
phoneNumber: {}
}
},
// Updates state when login is trigger
updateAuth : function(loggedIn) {
this.setState({
loggedIn: loggedIn
})
},
changeHPage: function() {
this.state.change = !this.state.change;
this.setState({
change: !this.state.change
});
console.log("changePage On HomePage Pressed");
this.context.router.push('/')
},
// Login even triggered and sent to back-end
componentWillMount : function() {
auth.onChange = this.updateAuth
auth.login()
},
addNumber: function(phonenumber){
this.state.phonenumber = phonenumber
this.setState()
},
// Renders App and all of its children
render : function() {
<div className="Detail">
{this.props.children && React.cloneElement(this.props.children, {
changeHPage: this.changeHPage
})}
</div>
var firstView;
{if(this.state.change) {
firstView = <div>
<div className="row">
<Veri> This is a child of Veri </Veri>
<Header details="Hi, I'm Plantee"/>
<section className="col s12">
<ul>
{this.state.loggedIn ? (
<div>
<li><Link to="/logout">Log out</Link> </li>
<li><Link to="/create">Create Your Plantee</Link></li>
{/*<Create> <Veri/> </Create>*/}
</div>
) : (
<div>
<li><Link to="/login">Log In</Link></li>
<li><Link to="/signup">Sign up</Link></li>
</div>
)}
<li><Link to="/about">About</Link></li>
</ul>
{this.props.children || <p>You are {!this.state.loggedIn && 'not'} logged in.</p>}
</section>
</div> </div>
} else {
firstView= <div>'Hello'</div>
}
return React.cloneElement(
firstView,
{switch: this.changeHPage}
)
}}
})
/* React router initialization */
var routes = (
<Router history={browserHistory}>
<Route path="/" component={App} >
<Route path="header" component={Header} />
<Route path="login" component={Login} />
<Route path="logout" component={Logout} />
<Route path="create" component={Create} change={App.changeHPage} />
<Route path="signup" component={Signup} />
<Route path="about" component={About} />
<Route path="very" component={Veri} />
</Route>
<Route path="*" component={NotFound} />
</Router>
)
ReactDOM.render(routes, document.querySelector('#container'))
create.js:
const React = require('react');
const ReactDOM = require('react-dom');
const auth = require('../helpers/auth')
const Veri = require('./veri.js');
const App = require('../app.js');
const ReactRouter = require('react-router');
// 4 components pulled from ReactRouter:
const Router = ReactRouter.Router;
const Route = ReactRouter.Route;
const Navigation = ReactRouter.Navigation;
const Link = ReactRouter.Link;
const browserHistory = ReactRouter.browserHistory;
const Create = React.createClass({
getInitialState: function(){
return {checked: false}
},
handleClick: function(event) {
event.preventDefault();
this.setState({checked: !this.state.checked})
let phonenumber = {
phonenumber: this.refs.phonenumber.value
}
},
showVerification : function(event) {
event.preventDefault();
},
remove(e) {
e.preventDefault();
console.log(this.props);
},
render : function(){
var msg;
{if(this.state.checked) {
msg = <div><Veri text={'Your verification code is '} code={'code'}/> <form className="gotIt" onSubmit={this.props.changeHpage} >
<input type="Submit" value="Got It" />
</form> </div>
}
else {
msg = <Veri details={''}/>
}}
return (
<div>
<h1>Create Your Plantee</h1>
<h2>Please Enter Your Phone Number</h2>
<p>You will recieve a phone call in order to verify that you are capable of raising a plantee</p>
<form className="telephoneNumber" onSubmit={this.handleClick}>
<input id="phonenumber" ref="phonenumber" type="tel" />
<input type="Submit" />
</form>
<div> {msg} </div>
<h3>{this.props.children}</h3>
</div>
)
}
})
module.exports = Create;
Please see the following github issue:
https://github.com/reactjs/react-router/issues/1857
this is directly taken from: ryanflorence
Usually if you're passing props across route boundaries your parent route knows exactly what it's rendering:
<Route path="/inbox" component={Inbox}>
<Route path=":messageId" component={Message}/>
<IndexRoute component={InboxStats}/>
</Route>
const Inbox = React.createClass({
render() {
return (
<div>
{/* this is only ever `Message`, except the weird case
of `InboxStats` which doesn't need the prop */}
{React.cloneElement(this.props.children, {
onDelete: this.handleMessageDelete
})}
</div>
)
}
})
Instead, use a componentless route and just do "normal" React stuff.
<Route path="/inbox" component={Inbox}>
{/* no more `Message` */}
<Route path=":messageId"/>
</Route>
const Inbox = React.createClass({
render() {
const { messageId } = this.props.params
return (
<div>
{messageId ? (
<Message onDelete={this.handleMessageDelete}/>
) : (
<InboxStats/>
)}
</div>
)
}
})
cloneElement is not bad practice on its own, but it can often be an indicator that there's a bit more straightforward way of doing something.

redux-form: Dynamically defining handleSubmit

Since I am pretty new to the React ecosystem my description and way of doing things may be way off but I hope you can follow my issue.
I have a parent Component that gets a form injected from the router and maps state and the action creators to the properties.
Container.js
import * as actionCreators from "../actionCreators";
export default class ComponentA extends React.Component {
render() {
return (
<div className="ComponentA">
{this.props.children} //<--Form
</div>
);
}
}
function mapStateToProps(state) {
return {
listItems: state.get("listItems")
};
}
export const Container = connect(mapStateToProps, actionCreators)(ComponentA);
The component that gets render with {this.props.children} is the following form.
Form.js
class Form extends React.Component {
static propTypes = {
fields: PropTypes.object.isRequired,
handleSubmit: PropTypes.func.isRequired
};
render() {
const { fields: {title, description}, handleSubmit} = this.props;
return (
<div className="create-project-form">
<h1>Create Project</h1>
<form onSubmit={handleSubmit}>
<label htmlFor="title">Title</label>
<input type="text" name="title" id="title" className="form-control"/>
<label htmlFor="description">Description</label>
<textarea name="description" id="" cols="30" rows="10" className="form-control"></textarea>
<button className="btn btn-danger" onClick={handleSubmit}>Create</button>
</form>
</div>
)
}
}
export default connectReduxForm({
form: "from",
fields: ["title", "description"]
})(Form);
Router
const routes = <Route component={App}>
<Route path="/" component={Container}/>
<Route path="/a" component={Container}>
<Route path="/a/create" component={Form}/>
</Route>
</Route>;
render(
<div>
<Provider store={store}>
<Router>{routes}</Router>
</Provider>
</div>,
document.getElementById("content"));
The Problem is handleSubmit is not undefined, but it is non of my actions. I actually don't expect that it is magically set to the correct actionCreator but how do I pass in the function? I tried the action name instead of handleSubmit but then the function is undefined. Every example I saw passes the handleSubmit function manually into the Form component, but I can't do that because the Form is set by the Router.
thanks
Two things:
You need to pass your field info to your <input>.
<input
type="text"
{...title} // <-------- that (it contains the "name" prop)
className="form-control"/>
You can pass any anonymous function to handleSubmit:
<form onSubmit={handleSubmit(data => {
// do your submit stuff here and return a Promise if you want the
// this.props.submitting flag to be set for the duration of the promise
})}>
Does that help? See also.

Categories