I have a component that looks like this (/Inputs/AddPersonForm):
import React, { Component } from 'react';
import { connect } from 'react-redux';
import {
BrowserRouter as Router,
Route,
Link,
Redirect,
withRouter
} from "react-router-dom";
import { addPerson } from '../Actions/personActions';
import { Person } from '../Components/Person'
export class AddPersonForm extends Component {
constructor(props) {
super(props)
this.state = {
//TODO initialize name to value that was just searched for to make UI
cleaner
name: '',
description: '',
location:'',
}
}
handleOnChange = event => {
const { value, name } = event.target;
this.setState({
[name]: value,
});
}
handleOnSubmit = event => {
const person = Object.assign({}, this.state);
event.preventDefault();
//mapdispatchtoprops not working when linking to this component
this.props.addPerson(person);
this.setState({
name: '',
description: '',
location:'',
});
}
componentDidMount() {
alert("The person you are searching for does not yet exist in our
system. Please add their name, a physical description, and their city,
state, and zip code.")
}
render() {
return (
<div className="container">
<div className="row">
<div className="col-md-8 col-md-offset-2">
<div className="panel panel-default">
<div className="panel-body">
<form className="form-horizontal" onSubmit={this.handleOnSubmit}>
<div className="form-group">
<label htmlFor="name" className="col-md-4 control-label">Person's Name</label>
<div className="col-md-5">
<input
className="form-control"
name="name"
value={this.state.name}
onChange={this.handleOnChange}
/>
</div>
</div>
<div className="form-group">
<label htmlFor="description" className="col-md-4 control-label">Physical Description</label>
<div className="col-md-5">
<textarea
className="form-control"
name="description"
value={this.state.description}
onChange={this.handleOnChange}
/>
</div>
</div>
<div className="form-group">
<label htmlFor="location" className="col-md-4 control-label">Location</label>
<div className="col-md-5">
<input
className="form-control"
type="text"
name="location"
value={this.state.location}
onChange={this.handleOnChange}
/>
</div>
</div>
<div className="form-group">
<div className="col-md-6 col-md-offset-4">
<button type="submit" className="btn btn-default">Add Person to Archive</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
);
}
}
const mapDispatchToProps = () => {
debugger;
return {
addPerson: addPerson
};
};
export default connect(null, mapDispatchToProps)(AddPersonForm);
I have a redirect to the that component and a link to it from this component (/Components/Person):
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { AddPersonForm } from '../Inputs/AddPersonForm';
import AddReviewForm from '../Inputs/AddReviewForm';
import { FormattedPerson } from '../Presentational/FormattedPerson';
import {
BrowserRouter as Router,
Route,
Link,
Redirect,
Switch
} from "react-router-dom";
export class Person extends Component {
render() {
if (this.props.person == "unfound") {
return (
<div>
{this.props.history.push('/add-person')}
</div>
)
} else {
return (
<div>
<div className= "container">
<Router>
<div>
<Link to={`${this.props.match.url}/reviews/new`}>
Add a New Review for this Person
</Link><br />
<Link to='/add-person'
>Not the {this.props.person.name} you were looking for? Add them to our system!
</Link>
<div>
<Switch>
<Route path={`${this.props.match.url}/reviews/new`}
component={AddReviewForm} />
<Route path='/add-person' component={AddPersonForm}/>
</Switch>
</div>
</div>
</Router>
</div>
<FormattedPerson name= {this.props.person.name} description= {this.props.person.description} location= {this.props.person.location} reviews= {this.props.person.reviews}
/>
</div>
);
}
}
}
function mapStateToProps(state){
return {person: state.peopleReducer.person}
}
export default connect(mapStateToProps, null)(Person);
When I navigate via the redirect, everything works fine, mapDispatchToProps is called, and I can submit the form. But when I navigate via the link, mapDispatchToProps is not called, so when I try to submit the form, I get an error saying "this.props.addPerson is not a function". Why is this happening?
You have two exports, one at the first line and one at the last one. Remove the first.
Related
I am trying to integrate Amplify Auth with a React application.
I am using AmplifyAuthenticator from #aws-amplify/ui-react package to customize the auth flow.
Though I can use AmplifySignIn component and customize it, I want to create a totally custom instead of AmplifySignIn.
Following is what my custom component looks like:
import React from 'react';
import { Auth } from 'aws-amplify';
import { useForm } from "react-hook-form";
export default function CustomSignIn(props){
const { register, reset, errors, handleSubmit } = useForm();
const onSubmit = async data => {
await Auth.signIn(data.username, data.password);
};
return(
<form className="mt-5" onSubmit={handleSubmit(onSubmit)} slot={props.slot}>
<h4>Login</h4>
<p>Need an account? <span>Create an account</span></p>
<div className="form-group">
<label>Email address</label>
<input type="username" name="username" className="form-control" ref={register({required: true})}/>
</div>
<div className="form-group">
<label>Password</label>
<input type="password" name="password" className="form-control" ref={register({required: true})}/>
</div>
<button type="submit" class="btn btn-primary btn-block">Continue</button>
</form>
);
}
Following is how I am trying to manage auth state:
import React, {useEffect} from 'react';
import { AmplifyAuthenticator } from '#aws-amplify/ui-react';
import '../styles/App.scss';
import { AuthState, onAuthUIStateChange } from '#aws-amplify/ui-components';
import ProtectedRoutes from './ProtectedRoutes';
import logo from '../assets/logo.svg';
import CustomSignIn from '../modules/auth/CustomSignIn';
function App() {
const [authState, setAuthState] = React.useState();
const [user, setUser] = React.useState();
useEffect(() => {
return onAuthUIStateChange((nextAuthState, authData) => {
setAuthState(nextAuthState);
setUser(authData)
});
}, []);
return ( authState === AuthState.SignedIn && user )
?
(
<ProtectedRoutes />
)
:
(
<div className="container-fluid container-fluid--auth">
<div className="row">
<div className="col-md-8">
<div className="row">
<div className="col-12 py-3">
<img className="logo" src={logo} alt="logo" />
</div>
</div>
<div className="row">
<div className="col-md-4 offset-md-4">
<AmplifyAuthenticator>
<CustomSignIn slot="sign-in" />
</AmplifyAuthenticator>
</div>
</div>
</div>
<div className="col-md-4 col--auth-sidebar">
</div>
</div>
</div>
);
}
export default App;
The thing is, how do I take the user to the Sign Up page when the user clicks on the "Create an account" button on the Sign In page?
i'm new learner of React.js and i don't finding the right solution.
i have the CreateUser component and when a client success at creating one i want to Redirect the client to App...
i need it to happend in this function of CreateUser Component
private handleSubmit(e:any){
e.preventDefault();
this.setState({
username: this.state.username,
password: this.state.password,
confirmPassword: this.state.confirmPassword,
userEmail: this.state.userEmail
})
this.passConfrim();
if (this.isAlertVisible){
console.log(this.state)
this.myUserDateService.create(this.state);
----> ** Right Here i need to redirect! ** <----
}
}
in the end of the function at the if statement
App:
import './App.css';
import { LoginComponent } from './components/LoginComponent';
import CreateUser from './components/CreateUser';
import "bootstrap/dist/css/bootstrap.min.css";
import { Router, Switch, Route, Link, useHistory as history} from "react-router-dom";
function App() {
return (
<Router history={history()} >
<nav className="navbar navbar-expand navbar-dark bg-dark">
<a href="/Home" className="navbar-brand">
To Do List
</a>
<div className="navbar-nav mr-auto">
<li className="nav-item">
<Link to={"/Login"} className="nav-link">
Login
</Link>
</li>
<li className="nav-item">
<Link to={"/CreateUser"} className="nav-link">
Create User
</Link>
</li>
</div>
</nav>
<div id="App-content">
<Switch >
<Route exact path={["/", "/Home"]} />
<Route path="/Login" exact component={LoginComponent} />
<Route path="/CreateUser" exact component={CreateUser} />
</Switch>
</div>
</Router>
);
}
export default App;
CreateUser Component:
import React, { Component } from 'react';
import { UserDataService } from '../services/UserData.service';
interface IState {
username:string;
userEmail:string;
password:string;
confirmPassword:string;
}
export class CreateUser extends Component <{}, IState> {
isAlertVisible: boolean = true;
myUserDateService = new UserDataService();
constructor(props: {}, myUserDateService:UserDataService){
super(props );
this.state = {
username:"",
password:"",
confirmPassword:"",
userEmail:"",
}
}
private handleSubmit(e:any){
e.preventDefault();
this.setState({
username: this.state.username,
password: this.state.password,
confirmPassword: this.state.confirmPassword,
userEmail: this.state.userEmail
})
this.passConfrim();
if (this.isAlertVisible){
console.log(this.state)
this.myUserDateService.create(this.state);
}
}
passConfrim(){
if(this.state.password !== this.state.confirmPassword){
this.isAlertVisible = false;
}else{
this.isAlertVisible = true;
}
}
render() {
return (
<div className="form-group">
<h1>Create User</h1>
<form onSubmit={e => this.handleSubmit(e)}>
<label >Username</label>
<input className="form-control" type="text" placeholder='Enter Username...' onChange={e => this.setState({username : e.target.value})} required/>
<br/>
<label >Email</label>
<input className="form-control" type="text" placeholder='Enter your email...' onChange={e => this.setState({userEmail : e.target.value})} required/>
<br/>
<label >Passowrd</label>
<input className="form-control" type="password" placeholder='Enter Password...' onChange={e => this.setState({password : e.target.value})} required/>
<br/>
<label >Confirm Passowrd</label>
<input className="form-control" type="password" placeholder='Confirm Password...' onChange={e => this.setState({confirmPassword : e.target.value })} required />
<div style={{color: "red", textAlign: "left"}} hidden={this.isAlertVisible}>**password not match</div>
<br/>
<button className="btn btn-primary" type="submit" >Create User</button>
</form >
</div>
)
}
}
export default CreateUser;
Basically you need not pass history with Router instead you can use withRouter high order component from react-router.
Import withRouter inside createUser component - https://reacttraining.com/react-router/core/api/withRouter
import { withRouter } from "react-router";
Then we just need to export CreateUser component like -
export default withRouter(CreateUser);
Now you have access to all props related to routing inside CreateUser component, now you can use -
this.props.history.push('/your-route');
To check what else properties you have with withRouter, you can just console.log this.props.history inside CreateUser component.
Tip - You cannot use hooks inside class components, so you cannot use useHistory inside CreateUser component instead use withRouter.
you can use history.push('/yourRoute') and that will take you to whatever route your heart so desires
Since you are extending the user component from react, it is a class component and you cannot use 'useHistory' hooks inside it.
Also you are passing history as a prop to router, can you try the below code to navigate and let me know.
this.props.history.push('/yourroute');
import React, { Component } from 'react';
import TaskBar from './Task';
class Todo extends Component {
state = {
todo: ''
}
changeHandler = (event) => {
console.log(event.target.value);
}
render() {
return (
<React.Fragment>
<div className="card">
<h5 className="card-header">Todo</h5>
<div className="card-body">
<h5 className="card-title">Task you want to do</h5>
<form>
<input type="text" className="form-control" value={this.state.todo} name="todo" onChange={(event) => this.changeHandler(event)} />
</form>
</div>
<button className="btn btn-primary">Go somewhere</button>
</div>
</React.Fragment>
)
}
}
export default Todo;
In the above code i don't know why i couldn't make any change
2) I am using Bootstrap cdn in my public folder and i am using these classes here
You forgot to set state inside your onchange handler.
import React, { Component } from 'react';
import TaskBar from './Task';
class Todo extends Component {
state = {
todo: ''
}
changeHandler = (event) => {
console.log(event.target.value);
this.setState({todo: event.target.value}) //you forgot to do this//
}
render() {
return (
<React.Fragment>
<div className="card">
<h5 className="card-header">Todo</h5>
<div className="card-body">
<h5 className="card-title">Task you want to do</h5>
<form>
<input type="text" className="form-control" value={this.state.todo} name="todo" onChange={(event) => this.changeHandler(event)} />
</form>
</div>
<button className="btn btn-primary">Go somewhere</button>
</div>
</React.Fragment>
)
}
}
export default Todo;
Link to a codesandbox example - https://codesandbox.io/s/jydjj?module=/example.js
Also currently your onchange uses an arrow function which creates a new function on every hit which is considered bad practice so i would suggest you to do this instead.
<input type="text" className="form-control" value={this.state.todo} name="todo" onChange={this.changeHandler} />
I am creating edit form.First i have to get data to edit form and i am calling it in componentDidMount().Please see code below.
import React from 'react';
import CompanyForm from './CompanyForm';
import { connect } from 'react-redux';
import { companyActions } from '../../../redux/actions/company-action';
class EditCompanyPage extends React.Component {
constructor(props){
super(props);
};
componentDidMount () {
const { id } = this.props.match.params
const { dispatch } = this.props;
dispatch(companyActions.getCompany(id));
}
render(){
const {editUser } = this.props;
return(
<div>
<h1>Edit Company</h1>
{
editUser && <CompanyForm handleActionParent={this.handleAction} companyDataFP={editUser} />
}
</div>
);
};
}
function mapStateToProps(state) {
const { editUser } = state.companyReducer;
return {
editUser
};
}
const EditCompany = connect(mapStateToProps)(EditCompanyPage);
export default EditCompany;
see code for CompanyForm component below:
import React from 'react';
class CompanyForm extends React.Component {
constructor(props){
super(props);
this.state = {
company :{
name : this.props.companyDataFP.name || '',
address1 : this.props.companyDataFP.address1 || '',
}
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
};
handleChange(e) {
const { name, value } = e.target;
const newState = Object.assign({}, this.state);
newState.company[name] = value;
this.setState(newState);
}
handleSubmit(e) {
e.preventDefault();
return false;
}
render(){
return(
<div className="col-md-12">
<form onSubmit={this.handleSubmit}>
<div className="row">
<div className="col-md-6">
<div className='form-group'>
<label htmlFor="name">Name</label>
<input type="text" name="name" className="form-control" onChange={this.handleChange} value={this.state.company.name} />
</div>
</div>
<div className="col-md-6">
<div className='form-group'>
<label htmlFor="address1">Address 1</label>
<input type="text" name="address1" className="form-control" onChange={this.handleChange} value={this.state.company.address1} />
</div>
</div>
</div>
<div className="row">
<div className="col-md-12">
<div className='form-group'>
<input type="submit" className="btn btn-info" value="submit" />
</div>
</div>
</div>
</form>
</div>
);
};
}
export default CompanyForm;
It works fine when i access this form with
<Link to="/edit-form/:id" >Edit</Link>
but when i refresh the current page then values are not rendering into form to edit.
I am using redux approach for state management, please guide me i am new to react.
Probably ComponyForm initializes form on its componentDidMount lifecycle function, so when editUser arrives nothing will change.
A way to handle this is changing:
<CompanyForm handleActionParent={this.handleAction} companyDataFP={editUser} />
to:
{editUser.name && <CompanyForm handleActionParent={this.handleAction} companyDataFP={editUser} />}
Hi I need to pass two parameters, to the class Chat. Currently it is getting only one parameter and displaying correctly.
const Chat = props => (
<div >
<ul>{props.messages.map(message => <li key={message}>{message}</li>)}</ul>
</div>
);
This Chat.js file is called from the Home.js. Suppose I need to pass the Chat component two parameters and I tried it like following.
import React, { Component } from 'react';
import { User } from './User';
import Chat from './Chat';
export class Home extends Component {
displayName = Home.name
state = {
messages: [],
names: []
};
handleSubmit = (message,name) =>
this.setState(currentState => ({
messages: [...currentState.messages, message],
names: [...currentState.names,name]
}));
render() {
return (
<div>
<div>
<User onSubmit={this.handleSubmit} />
</div>
<div>
<Chat messages={this.state.messages,this.state.name} />
</div>
</div>
);
}
}
In this scenario how should I change the Chat component to accept two parameters and display inside div tags.
This is what I tried. But seems it is incorrect.
const Chat = props => (
<div >
<ul>{props.messages.map((message, name) => <li key={message}>{message}</li> <li key={name}>{name}</li>)}</ul>
</div>
);
PS: The User Method
import * as React from 'react';
export class User extends React.Component{
constructor(props) {
super(props);
this.state = {
name: '',
message: ''
}
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
render() {
return (
<div className="panel panel-default" id="frame1" onSubmit={this.handleSubmit}>
<form className="form-horizontal" action="/action_page.php" >
<div className="form-group">
<label className="control-label col-sm-2" htmlFor="name">Your Name </label>
<div className="col-sm-10">
<input type="text" className="form-control" name="name" placeholder="Enter your Name" onChange={this.handleChange} />
</div>
</div>
<div className="form-group">
<label className="control-label col-sm-2" htmlFor="message">Message</label>
<div className="col-sm-10">
<input type="text" className="form-control" name="message" placeholder="Enter your Message" onChange={this.handleChange}/>
</div>
</div>
<div className="form-group">
<div className="col-sm-offset-2 col-sm-10">
<button type="submit" id="submit" className="btn btn-default">Submit</button>
</div>
</div>
</form>
</div>
);
}
handleChange(evt) {
this.setState({ [evt.target.name]: evt.target.value });
}
handleSubmit = (e) => {
e.preventDefault();
this.props.onSubmit(this.state.message, this.state.name);
this.setState({ message: "" });
this.setState({name:""});
};
}
You can do this by using separate attributes to pass different props. So for instance, you might revise your <Home/> components render method like so:
<Chat messages={this.state.messages} names={this.state.names} />
and then to access these two bits of data (messages and name) from inside the <Chat /> component you could do the following:
const Chat = props => (
<div >
<ul>{props.messages.map((message, index) => <li key={message}>
From: { Array.isArray(props.names) ? props.names[index] : '-' }
Message: {message}</li>)}
</ul>
</div>
);
Hope this helps!
You have to pass them separately:
<Chat messages={this.state.messages} name={this.state.name} />