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
Related
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.
I've got a parent component in which I initialize some piece of state, which I then pass down to the children components so that they can update that. However, when the update is triggered, the component tree is re-rendered and my inputs lose focus. Adding a key did not help.
// App.tsx
export function App(props) {
const useVal = useState("");
return (
<Router>
<Switch>
<Route
exact
path="/"
component={() => (
<StartScreen
useVal={useVal}
/>
)}
/>
// ...
</Router>
);
}
// StartScreen.tsx
interface StartScreenProps {
useVal: [string, React.Dispatch<React.SetStateAction<string>>];
}
function bindState<T>(
[value, setState]: [T, React.Dispatch<React.SetStateAction<T>>]
) {
return {
value,
onChange: ({ value }: { value: T }) => setState(value)
}
}
export const StartScreen = (props: StartScreenProps) => {
return (
<form>
<InputField
key="myInput"
{...bindState(props.useVal)}
/>
</form>
);
}
So, now when I start typing on my InputField (which is basically a wrapper on an <input>) on StartScreen.tsx, the input constantly loses focus as the component is totally re-rendered (I can see it in the DOM).
This happens because you are passing a function to the Route's component prop (I assume you are using react-router-dom) :
From the docs :
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.
To solve this problem use the render prop :
<Route
exact
path="/"
render={() => (
<StartScreen
useVal={useVal}
/>
)}
/>
This allows for convenient inline rendering and wrapping without the
undesired remounting explained above.
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;
I am having an issue with my application. My user component only loads UserCard when I start the application from the homepage then click users link there... if I just refresh the users URL... UserCard doesn't get loaded which means something is wrong with my this.props.users. I do see that in chrome it says: Value below was evaluated just now when I refresh but when I go through the flow it doesn't say that. Any help will be appreciated.
App.js
class App extends Component {
constructor(props) {
super(props);
this.state = {
users: []
};
}
componentDidMount() {
users = []
axios.get('/getall').then((res) => {
for(var d in res.data) {
users.push(new User(res.data[d]));
}
});
this.setState({ users });
}
render() {
const { users } = this.state;
return (
<Router history={history}>
<Switch>
<PrivateRoute exact path="/" component={Home} />
<Route exact path='/users' render={(props) => <Users {...props} users={users} />}/>
</Switch>
</Router>
)
}
}
PrivateRoute:
export const PrivateRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={props => (
<Component {...props} /> )} />
)
User.js
export default class Users extends Component {
render() {
console.log(this.props.users);
return (
<Row>
{this.props.users.map(u =>
<UserCard key={u.name} user={u}/>
)}
</Row>
);
}
}
export class User {
constructor(obj) {
for (var prop in obj){
this[prop] = obj[prop];
}
}
getURLName() {
return this.name.replace(/\s+/g, '-').toLowerCase();
}
}
class UserCard extends Component {
render() {
return (
<Link to={'/users/' + this.props.user.getURLName()} >
<div>
// Stuff Here
</div>
</Link>
);
}
}
As per the comments:
The issue here is how you're setting state. You should never modify state directly since this will not cause the component to rerender See the react docs
Some additional thoughts unrelated to the question:
As per the comments - use function components whenever possible, especially with hooks on the way
There is probably no need to create a User class, only to new up little user objects. Simply use plain old JS objects and calculate the link url right in the place its used:
render() {
const { user } = this.props
return <Link to={`/users/${user.name.replace(/\s+/g, '-').toLowerCase()}`} />
}
It might be a good idea to start using a linter such as eslint. I see that you're declaring users = [] without using let or const (don't use var). This is bad practice since creating variables in this way pollutes the global name space. Linters like eslint will help you catch issues like this while you're coding.
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.