I am getting the error that this.props.onAddTrip on components/NewTrip.jsx is not a function and I have been debugging this for a few days. Any help is appreciated! I have included the code to my NewTrip component, addTrip container and my redux action.
components/NewTrip.jsx
import React from 'react';
import ReactDOM from 'react-dom';
class NewTrip extends React.Component {
constructor(props) {
super(props)
this.state = {
location: '',
start: '',
end: ''
}
this.handleInputChange = this.handleInputChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
this.handleReset = this.handleReset.bind(this)
}
handleInputChange(e){
this.setState({
[e.target.name]: e.target.value
});
};
handleSubmit(e) {
e.preventDefault();
if(this.state.location.trim() && this.state.start.trim() &&
this.state.end.trim()) {
this.props.onAddTrip(this.state);
this.handleReset();
}
};
handleReset(){
this.setState({
location: '',
start: '',
end: ''
});
};
render() {
return (
<div className="container">
<form className="add_trip" onSubmit={ this.handleSubmit }>
<input name="location" className="start_form" type="text" autocomplete="off" placeholder=" Location" onChange={ this.handleInputChange } value={ this.state.location }/>
<input name="start" type="date" onChange={ this.handleInputChange } value={ this.state.start }/>
<input name="end" type="date" onChange={ this.handleInputChange } value={ this.state.end }/>
<input className="end_form" type="submit" value="Add" />
</form>
</div>
)
}
}
export default NewTrip;
containers/addTrip.js
import React from 'react';
import { connect } from 'react-redux';
import { addTrip } from '../actions';
import NewTrip from '../components/NewTrip.jsx';
const mapDispatchToProps = dispatch => {
return {
onAddTrip: trip => {
dispatch(addTrip(trip));
}
};
};
export default connect(
null,
mapDispatchToProps
)(NewTrip);
actions/index.js
import axios from 'axios';
export const addTrip = ( {location, start, end} ) => {
return (dispatch) => {
return axios.post('/api/trips', { location, start, end})
.then(response => {
dispatch(addTripSuccess(response.data))
})
.catch(error => {
throw(error)
})
}
}
export const addTripSuccess = data => {
return {
type: 'ADD_TRIP',
payload: {
// id: data.row.split(",")[0].substring(1),
id: data._id,
location: data.location,
start: data.start,
end: data.end
}
}
}
I'm a little confused about the purpose of the addTrip.js. If you move the logic in that file into your NewTrip component, your function should be defined.
import React from 'react';
import ReactDOM from 'react-dom';
import { connect } from 'react-redux'
import { addTrip } from '../actions'
class NewTrip extends React.Component {
constructor(props) {
super(props)
this.state = {
location: '',
start: '',
end: ''
}
this.handleInputChange = this.handleInputChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
this.handleReset = this.handleReset.bind(this)
}
handleInputChange(e){
this.setState({
[e.target.name]: e.target.value
});
};
handleSubmit(e) {
e.preventDefault();
if(this.state.location.trim() && this.state.start.trim() &&
this.state.end.trim()) {
this.props.addTrip(this.state);
this.handleReset();
}
};
handleReset(){
this.setState({
location: '',
start: '',
end: ''
});
};
render() {
return (
<div className="container">
<form className="add_trip" onSubmit={ this.handleSubmit }>
<input name="location" className="start_form" type="text" autocomplete="off" placeholder=" Location" onChange={ this.handleInputChange } value={ this.state.location }/>
<input name="start" type="date" onChange={ this.handleInputChange } value={ this.state.start }/>
<input name="end" type="date" onChange={ this.handleInputChange } value={ this.state.end }/>
<input className="end_form" type="submit" value="Add" />
</form>
</div>
)
}
}
export default connect(null, { addTrip })(NewTrip);
Give that a try. Also changed this.props.onAddTrip to this.props.addTrip (in handleSubmit) since thats the name of your imported function.
Related
I am trying to setup a SingleDatePicker as below but the import for 'react-dates/initialize' is showing hint error as below
module "c:/Users/LEE/Programming/ReactCourse/my-provider/node_modules/react-dates/initialize"
Could not find a declaration file for module 'react-dates/initialize'.
'c:/Users/LEE/Programming/ReactCourse/my-provider/node_modules/react-dates/initialize.js'
implicitly has an 'any' type. If the 'react-dates' package actually
exposes this module, consider sending a pull request to amend
'https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/react-dates'ts(7016)
Please advise
ExpenseForm.js
import React from "react";
import moment from "moment";
import 'react-dates/lib/css/_datepicker.css'
import 'react-dates/initialize';
import { SingleDatePicker } from "react-dates";
export default class ExpenseForm extends React.Component {
state = {
description: '',
note: '',
amount:'',
createdAt: moment(),
calenderFocused: false
}
onDescriptionChange = (e) =>{
const description = e.target.value
this.setState(()=>({description}))
}
onNoteChange = (e) => {
const note = e.target.value
this.setState(()=> ({note}))
}
onAmountChange = (e) => {
const amount = e.target.value
if (amount.match(/^\d*(\.\d{0,2})?$/)) {
this.setState(()=>({amount}))
}
}
onDateChange = (createAt) => {
this.setState(()=>({createAt}))
}
onFocusChange = ({focused}) => {
this.setState(()=>{calenderFocused: focused})
}
render(){
return (
<div>
<input
type='text'
placeholder="description"
autoFocus
value={this.state.description}
onChange={this.onDescriptionChange}
></input>
<input
type='text'
placeholder="amount"
value={this.state.amount}
onChange={this.onAmountChange}
></input>
<SingleDatePicker
date={this.state.createdAt} // momentPropTypes.momentObj or null
onDateChange={this.onDateChange} // PropTypes.func.isRequired
focused={this.state.calenderFocused} // PropTypes.bool
onFocusChange={this.onFocusChange} // PropTypes.func.isRequired
numberOfMonths={1}
isOutsideRange={()=>false}
/>
<textarea
placeholder="Please write a note (optional)"
value={this.state.note}
onChange={this.onNoteChange}
></textarea>
<button>Submit</button>
</div>
)
}
}
I want to change state by the value of input but it is undefined.
It should work, the value is for every HTML tag so what is wrong with my code??
import React, { Component } from "react";
class App extends Component {
constructor() {
super();
this.state = {
firstName: " "
};
this.handleChange = this.handleChange(this);
}
handleChange(event) {
this.setState({
firstName: event.target.value
});
}
render() {
return (
<form>
<input
type="text"
placeholder="Firstname"
onChange={this.handleChange}
/>
<h1>{this.state.value}</h1>
</form>
);
}
}
export default App;
There's no value stored in your state, so it is undefined, and you do not correctly bind this to your handler. Output this.state.firstName instead:
import React, { Component } from "react";
class App extends Component {
constructor() {
super();
this.state = {
firstName: " "
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({
firstName: event.target.value
});
}
render() {
return (
<form>
<input
type="text"
placeholder="Firstname"
onChange={this.handleChange}
/>
<h1>{this.state.firstName}</h1>
</form>
);
}
}
export default App;
First try to bind this for handleChange using an arrow function. Also use the state variable to set the value property of the input .
Sandbox link: https://codesandbox.io/s/react-basic-example-qmyw5
import React, { Component } from "react";
class App extends Component {
constructor() {
super();
this.state = {
firstName: " "
};
}
handleChange = (event) => {
this.setState({
firstName: event.target.value
});
}
render() {
return (
<form>
<input
type="text"
placeholder="Firstname"
onChange={this.handleChange}
value={this.state.firstName}
/>
<h1>{this.state.firstName}</h1>
</form>
);
}
}
export default App;
In user login form I am when I click login button it gives a token.
I am setting up the token in local storage and redirecting to 'customers' page.The page gets redirected to 'customers' but the page is not rendered.I have added a console.log in customer Page to check the whether the page is rendering or not.In this case it is not rendering it.
LoginForm
import React from 'react';
import _ from 'lodash';
import axios from '../config/Axios';
class LoginForm extends React.Component {
constructor() {
super()
this.state = {
email: '',
password: '',
error: '',
}
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
}
handleChange(e) {
this.setState({
[e.target.name]: e.target.value
})
}
handleSubmit(e) {
e.preventDefault()
const loginData = {
email: this.state.email,
password: this.state.password,
}
axios.post('/users/login', loginData, {
headers: {
'x-auth': localStorage.getItem('token')
}
})
.then((response) => {
console.log(response.data)
if (response.data.error) {
const error = response.data.error
this.setState({ error })
}
else {
const token = response.data.token
localStorage.setItem('token', token);
this.props.history.push('/customers')
}
})
.catch(err => console.log(err))
}
render() {
return (
<div className="form-group container">
<h1>Login</h1>
<form onSubmit={this.handleSubmit}>
<br />
<label htmlFor="email-login">Email</label>
<br />
<input type="email" value={this.state.email} onChange={this.handleChange} name="email" id="email-login" />
<br />
<label htmlFor="password-login">Password</label>
<br />
<input type="password" value={this.state.password} onChange={this.handleChange} name="password" id="password-login" />
<br />
<button type="submit" className="btn btn-info">Login</button>
{
!_.isEmpty(this.state.error) && <h3>{this.state.error}</h3>
}
</form>
</div>
)
}
}
export default LoginForm
Customer Page
class CustomerList extends React.Component {
constructor() {
super()
this.state = {
customers: [],
isLoading: true,
}
this.handleChange = this.handleChange.bind(this)
this.handleSave = this.handleSave.bind(this)
}
componentDidMount() {
axios.get('/customers', {
headers: {
'x-auth': localStorage.getItem('token')
}
})
.then(response => {
console.log(response.data)
const customers = response.data
this.setState({ customers, isLoading: false })
return customers
})
.catch(err => console.log(err))
}
handleChange(text) {
console.log(this.state.customers)
const customerDatas = this.state.customers
const customers = customerDatas.filter((customerData => {
return customerData.name.toLowerCase().includes(text)
}))
if (this.state.customers.length) {
this.setState({ customers })
}
else {
console.log(`${customers}......`)
this.componentDidMount()
}
}
render() {
console.log('customer localStorage', localStorage)
return (
<div>
<br />
<SearchBox handleChange={this.handleChange} />
<br />
{this.state.isLoading ? (
<Loader />
) : (
<div>
<h3>Progress Bar</h3>
</div>
)}
<br />
</div>
)
}
}
export default CustomerList
As discussed in chat and comments - you did not have router added in your Router Component
Please add the below line
<Route path='/customers' exact={true} component={CustomerList} />
check what the this.props what will be shown is that content history or not undefined
and do you have a router to element
I crafted a reactjs crud app with help of a tutorial and it works great now. Now i am trying to merge two form together so that same form should be used for both add and update operation.
This is my allpost.js file
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Post from '../components/Post';
import EditComponent from '../components/editComponent';
class AllPost extends Component {
render() {
return (
<div>
<h1>All Posts</h1>
{this.props.posts.map((post) => (
<div key={post.id}>
{post.editing ? <EditComponent post={post} key={post.id} /> :
<Post key={post.id} post={post} />}
</div>
))}
</div>
);
}
}
const mapStateToProps = (state) => {
return {
posts: state
}
}
export default connect(mapStateToProps)(AllPost);
and this is my postForm.js file:
import React, { Component } from 'react';
import { connect } from 'react-redux'
class PostForm extends Component {
handleSubmit = (e) => {
e.preventDefault();
const title = this.getTitle.value;
const message = this.getMessage.value;
const data = {
id: new Date(),
title,
message,
editing: false
}
console.log(data)
this.props.dispatch({
type: 'ADD_POST',
data,
});
this.getTitle.value = '';
this.getMessage.value = '';
}
render() {
return (
<div>
<h1>Create Post</h1>
<form onSubmit={this.handleSubmit}>
<input required type="text" ref={(input)=>this.getTitle = input}
placeholder="Enter Post Title"/>
<br /><br />
<textarea required rows="5" ref={(input)=>this.getMessage = input} cols="28"
placeholder="Enter Post" />
<br /><br />
<button>Post</button>
</form>
</div>
);
}
}
export default connect()(PostForm);
and this is my editComponent.js file
import React, { Component } from 'react';
import { connect } from 'react-redux';
class EditComponent extends Component {
handleEdit = (e) => {
e.preventDefault();
const newTitle = this.getTitle.value;
const newMessage = this.getMessage.value;
const data = {
newTitle,
newMessage
}
this.props.dispatch({ type: 'UPDATE', id: this.props.post.id, data: data })
}
render() {
return (
<div>
<form onSubmit={this.handleEdit}>
<input required type="text" ref={(input) => this.getTitle = input}
defaultValue={this.props.post.title} placeholder="Enter Post Title" /><br /><br />
<textarea required rows="5" ref={(input) => this.getMessage = input}
defaultValue={this.props.post.message} cols="28" placeholder="Enter Post" /><br /><br />
<button>Update</button>
</form>
</div>
);
}
}
export default connect()(EditComponent);
and this is my post.js file:
import React, { Component } from 'react';
import { connect } from 'react-redux'
class Post extends Component {
render() {
return (
<div>
<h2>{this.props.post.title}</h2>
<p>{this.props.post.message}</p>
<button onClick={() => this.props.dispatch({type: 'EDIT_POST', id: this.props.post.id})}>EDIT
</button>
<button onClick={ () => this.props.dispatch({type: 'DELETE_POST', id: this.props.post.id}) }>DELETE
</button>
</div>
);
}
}
export default connect()(Post);
and this is my postReducer.js file:
const postReducer = (state = [], action) => {
switch(action.type) {
case 'ADD_POST':
return state.concat([action.data]);
case 'DELETE_POST':
return state.filter((post)=>post.id !== action.id);
case 'EDIT_POST':
return state.map((post)=>post.id === action.id ? {...post,editing:!post.editing}:post)
case 'UPDATE':
return state.map((post)=>{
if(post.id === action.id) {
return {
...post,
title:action.data.newTitle,
message:action.data.newMessage,
editing: !post.editing
}
} else return post;
})
default:
return state;
}
}
export default postReducer;
Can anyone please help me to achieve this? I tried a lot to use same form form for both add and update and i failed to achieve this.
I think it's better you create separate component for rendering form data(FormComponent) and separate components for edit(EditComponent) and add(AddComponent).
This way there will not be clutter in one component and no if/else conditions for different modes like edit or add, or in future copy mode.
This approach will add flexibility and enhances compositional pattern of react.
1) AddComponent
import React, { Component } from 'react';
import { connect } from 'react-redux'
class AddComponent extends Component {
handleSubmit = (title, message) => {
const data = {
id: new Date(),
title,
message,
editing: false
}
this.props.dispatch({
type: 'ADD_POST',
data,
});
}
render() {
return (
<div>
<h1>Create Post</h1>
<FormComponent
buttonLabel='Post'
handleSubmit={this.handleSubmit}
/>
</div>
);
}
}
export default connect()(AddComponent);
2) EditComponent
import React, { Component } from 'react';
import { connect } from 'react-redux';
class EditComponent extends Component {
handleSubmit = (newTitle, newMessage) => {
const data = {
newTitle,
newMessage
}
this.props.dispatch({ type: 'UPDATE', id: this.props.post.id, data: data })
}
render() {
return (
<div>
<FormComponent
buttonLabel='Update'
handleSubmit={this.handleSubmit}
/>
</div>
);
}
}
export default connect()(EditComponent);
3) FormComponent
import React, { Component } from 'react';
class FormComponent extends Component {
handleSubmit = (e) => {
e.preventDefault();
const title = this.getTitle.value;
const message = this.getMessage.value;
this.props.handleSubmit(title, message);
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input required type="text" ref={(input) => this.getTitle = input}
defaultValue={this.props.post.title} placeholder="Enter Post Title" /><br /><br />
<textarea required rows="5" ref={(input) => this.getMessage = input}
defaultValue={this.props.post.message} cols="28" placeholder="Enter Post" /><br /><br />
<button>{this.props.buttonLabel}</button>
</form>
);
}
}
export default FormComponent;
Hope that helps!!!
You can create your own Form component with a prop of editMode to control whether it's Create or Update.
import React, {Component} from 'react';
import PropTypes from 'prop-types';
class Form extends Component {
handleSubmit = e => {
e.preventDefault();
const {editMode, post} = this.props;
const title = this.titleRef.value;
const body = this.bodyRef.value;
if (editMode){
const data = {
title,
body
}
this.props.dispatch({type: 'UPDATE', id: post.id, data})
}
else {
const data = {
id: new Date(),
title,
message,
editing: false
}
this.props.dispatch({type: 'ADD_POST', data});
}
}
render() {
const {editMode, post} = this.props;
const pageTitle = editMode ? 'Edit Post' : 'Create Post';
const buttonTitle = editMode ? 'Update' : 'Post';
return (
<div>
<h1>{pageTitle}</h1>
<form onSubmit={this.handleSubmit}>
<input
required
type="text"
ref={input => this.titleRef = input}
placeholder="Enter Post Title"
defaultValue={post.title}
/>
<textarea
required
rows="5"
ref={input => this.bodyRef = input}
cols="28"
placeholder="Enter Post"
defaultValue={post.body}
/>
<button>{buttonTitle}</button>
</form>
</div>
);
}
}
Form.propTypes = {
editMode: PropTypes.bool,
post: PropTypes.object
}
Form.defaultProps = {
editMode: false, // false: Create mode, true: Edit mode
post: {
title: "",
body: ""
} // Pass defined Post object in create mode in order not to get undefined objects in 'defaultValue's of inputs.
}
export default Form;
It would be on create mode by default but if you wanna update the post you should pass editMode={true} to your form component.
Problem
It's not showing the elements within the item. It doesn't render anything at the moment.
List.js
import React from 'react';
const List = props => (
<div>
{
props.items.map((item, index) => {
return <div key={index}>
<h1>{item.name}</h1>
<p>{item.term}</p>
</div>
})}
</div>
);
export default List;
App.js
import React, {Component} from 'react'
import './App.css'
import List from './List';
class App extends Component {
constructor(props) {
super(props);
this.state = {
term: '',
name: '',
items: []
};
}
onChange = (event) => {
const { name, value } = event.target;
this.setState({ [name]: value });
}
onSubmit = (event) => {
event.preventDefault();
this.setState({
term: '',
name: '',
items: [
...this.state.items,
this.state.term,
this.state.name
]
});
}
render() {
const { term, name, items } = this.state;
return (
<div>
<form className="App" onSubmit={this.onSubmit}>
<input name="term" value={this.state.term} onChange={this.onChange}/>
<input name="name" value={this.state.name} onChange={this.onChange}/>
<button>Submit</button>
</form>
<List items={this.state.items} />
</div>
);
}
}
Issue was in onSubmit, you need to convert to object and than add
onSubmit = event => {
event.preventDefault();
this.setState({
term: "",
name: "",
items: [...this.state.items, { term: this.state.term, name: this.state.name}]
});
setTimeout(() => { console.log(this.state.items) }, 0)
};
https://codesandbox.io/s/p7p128w7mx