state value is undefined on Redirect - reactjs

I need to pass data to another page using react redirect method, but I'm getting undefined value of state.
Form Component:
onSubmit I'm getting values of state through console.log but these values are not passing to Show component
class RegisterForm extends React.Component{
//constructor here
// handleInputChange function here
submit(){
console.log(this.state);
const data = this.state;
return <Redirect to={{
pathname: '/show',
state: {data: data},
}}
/>
}
render(){
return(
<div>
<div class="row">
<div class="col-md-6 offset-md-3">
<br /><br />
<h3>Register Form</h3><br />
<form
action="show"
>
<div class="form-row">
<div class="form-group col-md-6">
<label>First Name :</label>
<input type="text" class="form-control" name="first_name" onChange={this.handleInputChange} />
</div>
<div class="form-group col-md-6">
<label>Last Name :</label>
<input type="text" class="form-control" name="last_name" onChange={this.handleInputChange} />
</div>
</div>
<div class="form-row">
<div class="col-md-12 text-center">
<button type="submit" class="btn btn-primary" onClick={()=>this.submit()}>Submit</button>
</div>
</div>
</form>
</div>
</div>
</div>
)
}
}
export default RegisterForm;
props.location.state // undefined
can you help me?

Form action - Attribute for form submission
The URL that processes the form submission.
Question
It redirects to show page because I've used action="show" can you
tell me why submit function is not calling
The submit function isn't linked to anything in the UI. The form has a button of type "submit", so when clicked the default form actions are taken, i.e. the form is submitted and tries to go to the page specified by the action. React doesn't quite work in this way.
Since the button type is already "submit" you can simply replace the action prop with the onSubmit callback.
<form onSubmit={submit}> ...
Now that the submit callback handler is being invoked you'll also want to not take the default submit action on the form (likely a page reload). Returning JSX won't work here either, it won't ever get rendered. In order to do the redirect you'll need to do this using the history object from the Router/Route. This assumes RegisterForm is rendered directly as component or render prop of a Route or has been decorated with the withRouter Higher Order Component.
submit(event) {
event.preventDefault();
console.log(this.state);
const data = this.state;
const { history } = this.props;
history.replace({
pathname: '/show',
state: { data },
});
}

I would recommend you to use Function Components insted of class components,
Class components are too old, Life becomes too much easier with function components, and react hooks. I suggest you to go though react fucntion components and react hooks.
here I converted your class component into the function component with some hooks,
in submit method, just use history.push('/show',state).
import React, { useState } from "react";
import { useHistory } from "react-router-dom";
const RegisterForm = () => {
const [state, setState] = useState({
first_name: "",
last_name: "",
});
const history = useHistory();
const handleInputChange = (key) => (e) => {
let value = e.target.value;
setState((s) => ({ ...s, [key]: value }));
};
const handleSubmit = (e) => {
history.push("/show", state);
};
return (
<div>
<div className="row">
<div className="col-md-6 offset-md-3">
<br />
<br />
<h3>Register Form</h3>
<br />
<div className="form-row">
<div className="form-group col-md-6">
<label>First Name :</label>
<input
type="text"
className="form-control"
name="first_name"
value={state.first_name}
onChange={handleInputChange("first_name")}
/>
</div>
<div className="form-group col-md-6">
<label>Last Name :</label>
<input
type="text"
className="form-control"
name="last_name"
value={state.last_name}
onChange={handleInputChange("last_name")}
/>
</div>
</div>
<div className="form-row">
<div className="col-md-12 text-center">
<button
className="btn btn-primary"
onClick={handleSubmit}
>
Submit
</button>
</div>
</div>
</div>
</div>
</div>
);
};
export default RegisterForm;
Now, In the component at /show route,
you can use,
import { useLocation } from "react-router-dom";
....
....
// inside the Component,
let location = useLocation();
console.log(location);
console.log(location.state); // you can see, your state is available here...
that's it!
let me know if you found some difficulties here.
good luck ;)

Related

Clicking on react button asks me to leave site

I am learning react and I have a component which as a 2 input fields and a button, at the moment, clicking on the button will display a message in console log, but when the button is clicked it displays a popup Leave site?, Changes that you made may not be saved.
this is my code in this component
import React, { useRef, useState, Component } from 'react'
import { useAuthState } from 'react-firebase-hooks/auth';
import { useCollectionData } from 'react-firebase-hooks/firestore';
import { signOut } from 'firebase/auth';
class InfoLgnTest extends Component {
render() {
this.state = {
user: null
}
return (
<div>
<div className="App">
<SignInWithEmailPassword />
</div>
</div>
)
}
}
function SignInWithEmailPassword() {
const emailRef = useRef()
const passwordRef = useRef()
const signIn = () => {
console.log("InfoLgnTest singin clicked")
}
return (
<>
<div className="form">
<form>
<div className="input-container">
<label>Username </label>
<input
name="email"
type="text"
ref={emailRef}
required
placeholder ="something#gmail.com"
/>
</div>
<div className="input-container">
<label>Password </label>
<input
type="text"
name="pass"
ref={passwordRef}
required
placeholder ="..."
/>
</div>
<div className="button-container">
<input type="submit" onClick={signIn}/>
</div>
</form>
</div>
</>
)
}
export default InfoLgnTest
This code has a form, by default form send data as a request on the same page, for resolve this:
Add onSubmit to form,
call preventDefault method from event
call the function signIn
Change <input type="submit" ... /> to <button type="submit">Send</button>
function SignInWithEmailPassword() {
const emailRef = useRef()
const passwordRef = useRef()
const signIn = () => {
console.log("InfoLgnTest singin clicked")
}
// new function to handle submit
const submitForm = (event) => {
event.preventDefault();
signIn();
}
return (
<>
<div className="form">
{/* Add onSubmit */}
<form onSubmit={submitForm}>
<div className="input-container">
<label>Username </label>
<input
name="email"
type="text"
ref={emailRef}
required
placeholder ="something#gmail.com"
/>
</div>
<div className="input-container">
<label>Password </label>
<input
type="text"
name="pass"
ref={passwordRef}
required
placeholder ="..."
/>
</div>
<div className="button-container">
{/* Change input to button */}
<button type="submit">Send</button>
</div>
</form>
</div>
</>
)
}

How to add no of fields dynamically by taking input from user react

I want to add the input no of fields given by the user when clicking on submit button.How to do this in react functional component.
screenshot:
I have an input field,when any user input on that field and submit,according to the input given by the user the no fields will be created.like in above screenshot if a user gives input 6 then 6 fields will be added
I am trying in this way,
import React, { useState } from 'react'
import cal from './image/bgimg.jpg'
function Home() {
const [state,setState]=useState({
semester:'',
credit:'',
sgpa:''
})
const [noOfSem,setNoOfSem]=useState()
const handleChange=(e)=>{
setState({...state,[e.target.name]:e.target.value})
}
const handleClick=()=>{
console.log('hyy',state.semester)
setNoOfSem([state.semester])
}
return (
<div className="container">
<div className="row">
<div className="col-md-6">
<img src={cal} alt="" className='imgcal img-fluid' />
</div>
<div className="col-md-6">
<div className="col-md">
<div className="form1">
<input type="number" value={state.semester} name='semester' onChange={handleChange} placeholder='Enter Total Semester' />
<button type="button" class="btn btn-success" onClick={handleClick}>Submit</button>
<div className="form2">
{noOfSem?.map((item,index)=>
<>
<input type="text" placeholder={`Enter your Semester ${index+1} credit`} key={index}/>
</>
)}
</div>
</div>
</div>
</div>
</div>
</div>
)
}
export default Home
thanks......
I think there's a few changes you can make to improve the code and get it working.
Firstly, I would avoid storing your number of semesters in both the state.semester and noOfSem state, as it means you have to update both of them to keep them in sync.
Given that your component only needs to know the number of semesters when the user presses Submit, you can remove the handleChangeCall and instead only access the value upon submit.
It is also good practice to make use of the <form onSubmit={}> and <input type='submit'> elements, to handle form submission. Instead of using the onClick event from a regular <button>. Some info here.
When using the form, you can then access the value of the semester input directly by storing a reference to it using useRef.
Then in order to iterate over the number of semester, you can construct a new Array to map over. One caveat here is that you have to call the array fill method.
See solution here:
import React, { useState, useRef } from "react";
function Home() {
const [state, setState] = useState({
semester: '',
credit: "",
sgpa: ""
});
const semesterInputRef = useRef();
const handleForm1Submit = (e) => {
e.preventDefault();
if (semesterInputRef?.current.value) {
setState({ ...state, semester: Number(semesterInputRef.current.value) });
}
};
return (
<div className="container">
<div className="row">
<div className="col-md-6">
<div className="col-md">
<form className="form1" onSubmit={handleForm1Submit}>
<input
type="number"
name="semester"
ref={semesterInputRef}
placeholder="Enter Total Semester"
></input>
<input type="submit" className="btn btn-success"></input>
</form>
<form className="form2">
{state.semester &&
Array(state.semester).fill().map((_item, index) => (
<input
key={index}
type="text"
placeholder={`Enter your Semester ${index + 1} credit`}
></input>
))}
</form>
</div>
</div>
</div>
</div>
);
}
export default Home;
I've also created a code sandbox to show that this works as expected, here.

Reactjs: How to clear text field after submission

I have a signup page which has 4 input text fields which changes the currentState of each field from an empty string to whatever I inputted. After signing up it logs each of the following 4 fields input in a database with a post request. How can I clear each field after clicking on the sign up button. Basically I just want the page to clear after clicking. I have attempted to set the state of the 4 variables back to an empty string at the end of my promise chain but nothing changes still.
import React,{useState} from 'react';
import style from './Signup.css';
import Axios from 'axios';
function Signup() {
const [firstReg, setFirstNameReg] = useState('')
const [lastReg, setLastNameReg] = useState('')
const [emailReg, setEmailReg] = useState('')
const [passReg, setPassReg] = useState('')
const register = () => {
Axios.post('http://localhost:3001/register', {
first_name: firstReg,
last_name: lastReg,
email: emailReg,
password: passReg,
}).then((response)=> {
console.log(response);
setFirstNameReg('')
});
};
return (
<div className="Signup">
<div className='Sign'>
<div class="photo"> Create an account</div>
<div class ='searche'>
<div className="searchInputse">
<input type="text" onChange={(e)=> {setFirstNameReg(e.target.value)}} placeholder={'First name'} />
<div className="searchIcone"></div>
</div>
<div className="dataResult"></div>
</div>
<div class ='searche'>
<div className="searchInputse">
<input type="text" onChange={(e)=> {setLastNameReg(e.target.value)}} placeholder={'Last name'}/>
<div className="searchIcone"></div>
</div>
<div className="dataResult"></div>
</div>
<div class ='searche'>
<div className="searchInputse">
<input type="text" onChange={(e)=> {setEmailReg(e.target.value)}} placeholder={'Email'}/>
<div className="searchIcone"></div>
</div>
<div className="dataResult"></div>
</div>
<div class ='searche'>
<div className="searchInputse">
<input type="text" onChange={(e)=> {setPassReg(e.target.value)}} placeholder={'Password'}/>
<div className="searchIcone"></div>
</div>
<div className="dataResult"></div>
</div>
<button className='searchee' onClick={register}>
Sign Up
</button>
</div>
</div>
);
}
export default Signup;
A few things you should be aware of.
1st. You want to keep your data in an object for easy management and code reduction like so:
Define the initial object outside of your component;
let initialValues = {
'first_name': '',
'last_name': '',
'email': '',
}
And inside your component define state with the initialValues variable as the state default.
const [data, setData] = useState(initialValues);
And then, in your JSX you can connect the values with the object keys like so:
<input type="text" name="first_name" value={data.first_name} />
<input type="text" name="last_name" value={data.last_name} />
<input type="text" name="email" value={data.email} />
You can also then add an onChange handler to that input like so:
note: name must match the key inside of the initalValues object.
<input type="text" name="first_name" value={data.first_name} onChange={handleChange} />
<input type="text" name="last_name" value={data.last_name} onChange={handleChange} />
<input type="text" name="email" value={data.email} onChange={handleChange} />
handleChange basic function can look like this:
const onChange = (e) => {
setData({...data, [e.target.name]: e.target.value})
}
Essentially all you're doing is typing inside the input field, onChange detects a change on each key press, and fires off the handleChange function, that function makes a copy of the current state of data then looks at the e.target.name which could be first_name and sets it to e.target.value which is anything you type.
That way, all you need to pass to axios is the data object.
I hope this helps, let me know if you have any other questions.
Happy coding!
Hi try to replace your register button with this :
<button className='searchee' onClick={()=>{register();setFirstNameReg('');setLastNameReg('');setEmailReg('');setPassReg('')}}>
Sign Up
</button>

Setting the default value of an input field after data is retrieved causes the content to overlap and the "onChange" event not to be triggered

I have an "edit category" component in my React application.
The ID is passed through the URL.
When the component is mounted, the action "fetchCategory" is called, which updates the props on the component with the current category.
I have a form which I want to be pre-populated, which I'm currently doing using the defaultValue on the input.
However, this isn't reflected on the state and the label for the text field overlaps the input field.
Any help would be greatly appreciated. I'll leave snippets of my code below which could help with understanding what I'm trying to do.
import React, { Component } from "react";
import { connect } from "react-redux";
import { fetchCategory } from "../../store/actions/categoryActions";
class AddOrEditCategory extends Component {
componentDidMount() {
this.props.fetchCategory(this.props.match.params.id);
if (this.props.match.params.id) {
this.setState({
_id: this.props.match.params.id
});
}
}
handleSubmit = e => {
e.preventDefault();
console.log(this.state);
};
handleChange = e => {
this.setState({
[e.target.id]: e.target.value
});
};
render() {
const addingNew = this.props.match.params.id === undefined;
return (
<div className="container">
<h4>{addingNew ? "Add category" : "Edit category"}</h4>
<form onSubmit={this.handleSubmit}>
<div className="input-field">
<input
type="text"
id="name"
defaultValue={this.props.category.name}
onChange={this.handleChange}
/>
<label htmlFor="name">Category name</label>
</div>
<div className="input-field">
<input
type="text"
id="urlKey"
onChange={this.handleChange}
defaultValue={this.props.category.urlKey}
/>
<label htmlFor="urlKey">URL Key</label>
</div>
<button className="btn">{addingNew ? "Add" : "Save"}</button>
</form>
</div>
);
}
}
const mapStateToProps = state => {
return {
category: state.categoryReducer.category
};
};
export default connect(
mapStateToProps,
{ fetchCategory }
)(AddOrEditCategory);
EDIT: Included whole component as requested
You need to replace the 'defaultValue' attribute with 'value' in the inputs.
You are using a controlled vs uncontrolled component. You dont need to use defaultValue.
You can set the initial values on the promise success for fetchCategory
componentDidMount() {
this.props.fetchCategory(this.props.match.params.id).then(response => {
// Set the initial state here
}
}
OR in
componentWillReceiveProps(nextProps) {
// Compare current props with next props to see if there is a change
// in category data received from action fetchCategory and set the initial state
}
React docs
<form onSubmit={this.handleSubmit}>
<div className="input-field">
<input
type="text"
id="name"
onChange={this.handleChange}
value={this.state.name} //<---
/>
<label htmlFor="name">Category name</label>
</div>
<div className="input-field">
<input
type="text"
id="urlKey"
onChange={this.handleChange}
value={this.state.urlKey}
/>
<label htmlFor="urlKey">URL Key</label>
</div>
<button className="btn">{addingNew ? "Add" : "Save"}</button>
</form>

How to accept and pass two parameter as props

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} />

Resources