After submitting my form I am not getting the submitted data on console and also the page is not routing
I am not getting the reason behind this. Searching for 2 days but still not got a suitable answer. Can anyone tell me the solution?
import axios from "axios";
import { BrowserRouter as Router, Link, Route, Switch } from "react-router-dom";
// import { createBrowserHistory as history } from "history";
import { withRouter } from "react-router-dom";
import DoneStatus from "./DoneStatus";
class Body extends React.Component {
titleDescMap = new Map();
constructor() {
super();
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChangeTitle = this.handleChangeTitle.bind(this);
this.handleChangeDescription = this.handleChangeDescription.bind(this);
this.handleCheckBoxChange = this.handleCheckBoxChange.bind(this);
}
state = {
countTodo: 1,
valueTitle: "",
valueDescription: "",
checkStatus: false,
routing: false,
};
statesStatus() {
return {
checkStatus: this.state.checkStatus,
};
}
handleChangeTitle(event) {
this.setState({
valueTitle: event.target.value,
});
}
handleChangeDescription(event) {
this.setState({
valueDescription: event.target.value,
});
}
handleCheckBoxChange(event) {
this.setState((prev) => ({ checkStatus: !prev.checkStatus }));
console.log(this.state.checkStatus);
}
handleSubmit(event) {
// Debugging my states
console.log("Id: " + this.state.id);
console.log("Title: " + this.state.valueTitle);
console.log("Description: " + this.state.valueDescription);
console.log("Check Status: " + this.state.checkStatus);
event.preventDefault();
var previousTitle = this.titleDescMap.has(this.state.valueTitle);
// Sending data to database
// Checking if any title and desc is previously stored
if (previousTitle) {
alert("Please Enter Another Title (which you have never used)");
} else {
// Setting the values in title and description into Map
this.titleDescMap.set(this.state.valueTitle, this.state.valueDescription);
console.log(this.titleDescMap);
// Updating id as counter increases 1
this.setState((previousState) => ({
countTodo: previousState.countTodo + 1,
}));
if (this.state.checkStatus) {
const backendData = {
countTodo: this.state.countTodo,
title: this.state.valueTitle,
description: this.state.valueDescription,
};
axios
.post("https://todo-list-site.herokuapp.com/todo-data", backendData)
.then((data) => {
console.log(data);
this.props.history.push("/submit");
})
.catch((err) => {
console.error("Error");
});
console.log(backendData);
}
}
}
render() {
console.log(this.state.checkStatus);
return (
<div className="body-container">
<p className="body-direction">Fill To Save Your Todo</p>
<form method="post" onSubmit={this.handleSubmit}>
<div className="form-group">
<label>Title</label>
<input
type="text"
className="form-control"
placeholder="Title here"
value={this.state.valueTitle}
onChange={this.handleChangeTitle}
/>
</div>
<div className="form-group">
<label>Description</label>
<br />
<textarea
className="form-control"
placeholder="Description here"
rows="4"
cols="40"
value={this.state.valueDescription}
onChange={this.handleChangeDescription}
/>
</div>
<div className="form-check">
<input
type="checkbox"
className="form-check-input"
onChange={this.handleCheckBoxChange}
/>
<label className="form-check-label body-input-label">
Save Permanently
</label>
</div>
<button
type="submit"
// onClick={() => history().push("/submit")}
className="btn btn-primary"
>
+ Add
</button>
</form>
</div>
);
}
}
export default withRouter(Body);
I have updated my code as per the given solution on this question. But still not getting the proper output
I want to send all the data in the form into my backend and render it on another page.
Data is submitting but I am not getting it on the console. Also routing is the major issue here.
I have added the file which includes all the routes
import Header from "./Header";
import Body from "./Body";
import DoneStatus from "./DoneStatus";
import Saved from "./Saved";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
// import Footer from "./Footer";
class App extends React.Component {
render() {
const body = new Body();
const checkStatus = body.statesStatus();
return (
<React.Fragment>
<Router>
<Header />
<Switch>
<Route
exact
path="/"
render={() => {
return (
<div className="app-container">
<Body />
</div>
);
}}
></Route>
</Switch>
<Switch>
<Route
exact
path="/saved"
render={() => {
return <Saved />;
}}
></Route>
</Switch>
{/* <Footer /> */}
<Switch>
<Route
exact
path="/submit"
render={() => {
return <DoneStatus checkedStatus={checkStatus.checkStatus} />;
}}
></Route>
</Switch>
</Router>
</React.Fragment>
);
}
}
export default App;
You are not waiting for your POST to finish, and as #Drew Reese points, you are calling the history wrong
handleSubmit(event) {
// Debugging my states
console.log("Id: " + this.state.id);
console.log("Title: " + this.state.valueTitle);
console.log("Description: " + this.state.valueDescription);
console.log("Check Status: " + this.state.checkStatus);
event.preventDefault();
var previousTitle = this.titleDescMap.has(this.state.valueTitle);
// Sending data to database
// Checking if any title and desc is previously stored
if (previousTitle) {
alert("Please Enter Another Title (which you have never used)");
} else {
// Setting the values in title and description into Map
this.titleDescMap.set(this.state.valueTitle, this.state.valueDescription);
console.log(this.titleDescMap);
// Updating id as counter increases 1
this.setState((previousState) => ({
countTodo: previousState.countTodo + 1,
}));
if (this.state.checkStatus) {
const backendData = {
countTodo: this.state.countTodo,
title: this.state.valueTitle,
description: this.state.valueDescription,
};
axios.post(
"https://todo-list-site.herokuapp.com/todo-data",
backendData
).then((response) => {
console.log(response);
history.push("/submit");
}).catch((error) => {
console.error(error);
});
}
}
}
Also, you are not waiting for the countTodo state to be setted after getting it to send it to the backend, that might get you unexpected behaviours some times
I think you are missing handling promise, please find below solution:
axios.get(`https://todo-list-site.herokuapp.com/todo-data`)
.then(backendData => {
console.log(backendData);
})
Related
I'm trying to route a page to my project that will allow the user to edit an existing data entry within a MongoDB database, however I am getting this error. The intended function of the program is that a user will be able to select an edit button on a specific data entry, which will open a form allowing them to altar that entry's content. It should do this by making a get request using the data's ID, however there is something wrong happening when I try to concatenate the route string, and the data's ID. Here is my Main.js file, my VehiclesList.js file, and my EditVehicles.js file:
Main.js
import React, { Component } from "react";
import "./index.css";
import { Routes, Route } from "react-router-dom";
import VehicleList from "../VehicleList/index";
import AddVehicle from "../addVehicle/index";
import EditVehicle from "../editVehicle";
class Main extends Component {
render() {
return (
<div className="main">
<Routes>
<Route exact path="/" element={<VehicleList />} />
<Route path="/list" element={<VehicleList />} />
<Route path="/addVehicle" element={<AddVehicle />} />
<Route path="/editVehicle/:id" element={<EditVehicle />} />
</Routes>
</div>
);
}
}
export default Main;
VehiclesList.js
import React, { Component, useState } from "react";
import "./index.css";
import axios from "axios";
import { Link } from "react-router-dom";
import VehicleService from "../services/services";
class VehicleList extends Component {
constructor(props) {
super(props);
this.VehicleService = new VehicleService();
this.state = {
vehicles: [],
};
this.deleteVehicle = this.deleteVehicle.bind(this);
}
componentDidMount = () => {
this.getVehicleList();
};
//get all vehicles
getVehicleList() {
axios
.get("http://localhost:5000/vehicles")
.then((res) => {
console.log(res);
this.setState({
vehicles: res.data,
});
})
.catch((err) => {
console.log(err);
});
}
//delete vehicle
deleteVehicle(vehicleId) {
this.VehicleService.deleteVehicle(vehicleId);
this.getVehicleList();
window.location.reload(false);
}
render() {
const { vehicles } = this.state;
return (
<div className="vehicleList">
<table className="vehicleList_table">
<thead>
<tr className="vehicleList_table_head">
<th className="vehicleList_table_head_title">#</th>
<th className="vehicleList_table_head_title">Make</th>
<th className="vehicleList_table_head_title">Model</th>
<th className="vehicleList_table_head_title">Year</th>
<th className="vehicleList_table_head_title">Price</th>
<th className="vehicleList_table_head_title">Status</th>
<th className="vehicleList_table_head_title"></th>
<th className="vehicleList_table_head_title"></th>
</tr>
</thead>
<tbody className="vehicleList_table_body">
{vehicles &&
vehicles.map((vehicle, idx) => {
let status = "";
if (vehicle.isSold) {
status = "Sold";
} else {
status = "Available";
}
return (
<tr key={idx}>
<td className="vehicleList_table_item">{vehicle._id}</td>
<td className="vehicleList_table_item">{vehicle.make}</td>
<td className="vehicleList_table_item">{vehicle.model}</td>
<td className="vehicleList_table_item">{vehicle.year}</td>
<td className="vehicleList_table_item">{vehicle.price}</td>
<td className="vehicleList_table_item">{status}</td>
<td className="vehicleList_table_item">
<Link
to={"/editVehicle/" + vehicle._id}
style={{ fontSize: "1.5rem" }}
className="edit_btn"
>
edit
</Link>
</td>
<td className="vehicleList_table_item">
<button
onClick={() => this.deleteVehicle(vehicle._id)}
className="delete_btn"
>
Delete
</button>
</td>
</tr>
);
})}
</tbody>
</table>
</div>
);
}
}
export default VehicleList;
EditVehicles.js
import React, { Component } from "react";
import axios from "axios";
import { Link } from "react-router-dom";
class EditVehicle extends Component {
constructor(props) {
super(props);
this.state = {
make: "",
model: "",
year: "",
price: "",
isSold: "",
};
}
componentDidMount = () => {
this.getVehicleById();
};
//get vehicle by ID
getVehicleById() {
axios
.get(
"http://localhost:5000/vehicles/editVehicle/" +
this.props.match.params.id
)
.then((res) => {
this.setState({
make: res.data.make,
model: res.data.model,
year: res.data.year,
price: res.date.price,
isSold: res.data.isSold,
});
})
.catch((err) => {
console.log(err);
});
}
//handle changes to data
changeHandler = (e) => {
this.setState({ [e.target.name]: e.target.value });
};
//update record on submit
submitHandler = (e) => {
e.preventDefault();
const { make, model, year, price, isSold } = this.state;
axios
.post(
"http://localhost:5000/vehicles/editVehicle/" +
this.props.match.params.id,
{
make: make,
model: model,
year: year,
price: price,
isSold: isSold,
}
)
.then((res) => {
console.log(res);
this.props.history.push("/");
})
.catch((err) => {
console.log(err);
});
};
render() {
return (
<div className="editVehicle">
<form className="editVehicle_form" onSubmit={this.submitHandler}>
<Link to="/" className="back_btn">
Back
</Link>
<h1>Update Vehicle Details</h1>
<p>Please fill out all fields</p>
<input
name="make"
placeholder="Make"
type="text"
value={this.state.make}
onChange={this.changeHandler}
className="editVehicle_form_input"
/>
<br />
<input
name="model"
placeholder="Model"
type="text"
value={this.state.model}
onChange={this.changeHandler}
className="editVehicle_form_input"
/>
<br />
<input
name="year"
placeholder="Year"
type="text"
value={this.state.year}
onChange={this.changeHandler}
className="editVehicle_form_input"
/>
<br />
<input
name="price"
placeholder="Price"
type="text"
value={this.state.price}
onChange={this.changeHandler}
className="editVehicle_form_input"
/>
<br />
<label className="addVehicle_form_radio_label">
Available
<input
name="isSold"
type="radio"
value={false}
onChange={this.changeHandler}
className="editVehicle_form_radio"
/>
</label>
<label className="editVehicle_form_radio_label">
Sold
<input
name="isSold"
type="radio"
value={true}
onChange={this.changeHandler}
className="editVehicle_form_radio"
/>
</label>
<br />
<input type="submit" value="Save" className="editVehicle_submit" />
</form>
</div>
);
}
}
export default EditVehicle;
My Routes to the VehiclesList, AddVehicle and DeleteVehicle pages all work. the only issue I'm running into is routing the EditVehicle page.
at first I was getting the error "Cannot read properties of undefined('params'), and then I changed the route to the EditVehicles page to this:
<Route path="/editVehicle/:id" component={EditVehicle} />
when I did it this way, I got an error back saying that this route did not have an element, and would result in an empty page.
I also tried doing it this way:
<Route path='/editVehicle/:id render={(props) => <EditVehicle {...props} />} />
this too just gave me an error saying that the route did not have an element.
is there an issue in my code that I am not seeing? or is there an easier way to do this?
The error tells you, that you don't have element attribute in Route:
<Route path="/editVehicle/:id" component={EditVehicle} />
Replace component to element
<Route path="/editVehicle/:id" element={<EditVehicle/>} />
You messed up with different versions of React-router-dom implementations.
see docs
Try to add useParams hook from React-router-dom in your EditVehicle component to get id:
let { id } = useParams();
Issue
The "edit vehicle" route is correct:
<Route path="/editVehicle/:id" element={<EditVehicle />} />
The issue is that in react-router-dom#6 there are no longer any route props. In other words, EditVehicle is a class-based component and this.props.match and this.props.history are undefined. It's only when the code attempts to access into this undefined object (i.e. this.props.match.params) that the error is thrown.
Additionally, the history object is no longer directly exposed in RRDv6, you'll need to use the useNavigate hook to access a navigate function.
Solution
You can either convert EditVehicle to a React function component or create a new custom withRouter Higher Order Component so that the route path params can be injected as props like the class component is expecting.
Using custom withRouter HOC
Create a withRouter HOC that can use the React hooks and inject new props for the class component to access.
import { useNavigate, useParams, /* other hooks */ } from 'react-router-dom';
const withRouter = Component => props => {
const navigate = useNavigate();
const params = useParams();
// ... other hooks ...
return (
<Component
{...props}
{...{ navigate, params, /* other props from hooks */ }}
/>
);
};
Decorate the EditVehicle component export:
import withRouter from '../path/to/withRouter';
...
export default withRouter(EditVehicle);
params and navigate (and others) are now injected as props, access accordingly in EditVehicle:
// get vehicle by ID
getVehicleById() {
const { params: { id } } = this.props;
axios.get("http://localhost:5000/vehicles/editVehicle/" + id)
.then((res) => {
const { data: { make, model, year, price, isSold } } = res;
this.setState({ make, model, year, price, isSold });
})
.catch(console.log);
}
// update record on submit
submitHandler = (e) => {
e.preventDefault();
const { make, model, year, price, isSold } = this.state;
const { navigate, params: { id } } = this.props;
axios
.post(
"http://localhost:5000/vehicles/editVehicle/" + id,
{ make, model, year, price, isSold }
)
.then((res) => {
console.log(res);
navigate("/");
})
.catch(console.log);
};
Conversion to React function component
Use the useNavigate and useParams hooks directly
Use React.useState to store the form field values
use React.useEffect to update the state when the id path param updates
Convert all class methods to functions, and remove references to this
Code:
import React, { useEffect, useState } from "react";
import axios from "axios";
import { Link, useParams, useNavigate } from "react-router-dom";
const EditVehicle = () => {
const navigate = useNavigate();
const { id } = useParams();
const [state, setState] = useState({
make: "",
model: "",
year: "",
price: "",
isSold: ""
});
useEffect(() => {
// get vehicle by ID
const getVehicleById = (id) => {
axios
.get("http://localhost:5000/vehicles/editVehicle/" + id)
.then((res) => {
const { make, model, year, price, isSold } = res.data;
setState({ make, model, year, price, isSold });
})
.catch(console.log);
};
getVehicleById(id);
}, [id]);
//handle changes to data
const changeHandler = (e) => {
const { name, value } = e.target;
setState((state) => ({
...state,
[name]: value
}));
};
//update record on submit
const submitHandler = (e) => {
e.preventDefault();
const { make, model, year, price, isSold } = state;
axios
.post("http://localhost:5000/vehicles/editVehicle/" + id, {
make,
model,
year,
price,
isSold
})
.then((res) => {
console.log(res);
navigate("/");
})
.catch(console.log);
};
return (
<div className="editVehicle">
<form className="editVehicle_form" onSubmit={submitHandler}>
<Link to="/" className="back_btn">
Back
</Link>
<h1>Update Vehicle Details</h1>
<p>Please fill out all fields</p>
<input
name="make"
placeholder="Make"
type="text"
value={state.make}
onChange={changeHandler}
className="editVehicle_form_input"
/>
<br />
<input
name="model"
placeholder="Model"
type="text"
value={state.model}
onChange={changeHandler}
className="editVehicle_form_input"
/>
<br />
<input
name="year"
placeholder="Year"
type="text"
value={state.year}
onChange={changeHandler}
className="editVehicle_form_input"
/>
<br />
<input
name="price"
placeholder="Price"
type="text"
value={state.price}
onChange={changeHandler}
className="editVehicle_form_input"
/>
<br />
<label className="addVehicle_form_radio_label">
Available
<input
name="isSold"
type="radio"
value={false}
onChange={changeHandler}
className="editVehicle_form_radio"
/>
</label>
<label className="editVehicle_form_radio_label">
Sold
<input
name="isSold"
type="radio"
value={true}
onChange={changeHandler}
className="editVehicle_form_radio"
/>
</label>
<br />
<input type="submit" value="Save" className="editVehicle_submit" />
</form>
</div>
);
};
I am developing a simple application of notes, and when I try to edit the notes, I get the error "No routes matched location id ..."
What am I doing wrong?
I try to get the id of the note by params
This is my code:
Notes.js:
import React from "react";
import Header from "./notes/Header";
import Home from "./notes/Home";
import CrearNota from "./notes/CrearNota";
import EditarNota from "./notes/EditarNota";
import { BrowserRouter, Routes, Route, Link } from "react-router-dom";
export default function Notes({ setIsLogin }) {
return (
<header>
<BrowserRouter>
<Header setIsLogin={setIsLogin} />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/create" element={<CrearNota />} />
<Route path="/edit/:id" element={<EditarNota />} />
</Routes>
</BrowserRouter>
</header>
);
}
And EditarNotas.js:
import { useState, useEffect } from "react";
import axios from "axios";
import { useNavigate } from "react-router-dom";
export default function EditarNota(match) {
const [note, setNote] = useState({
title: "",
content: "",
date: "",
id: "",
});
const navigate = useNavigate();
useEffect(() => {
const getNote = async () => {
const token = localStorage.getItem("tokenStore");
if (match.params.id) {
const res = await axios.get(`/api/notes/${match.params.id}`, {
headers: { Authorization: token },
});
console.log(match.params.id);
console.log(res);
setNote({
title: res.data.title,
content: res.data.content,
date: res.data.date,
id: res.data._id,
});
}
};
getNote();
}, [match.params.id]);
const onChangeInput = (e) => {
const { name, value } = e.target;
setNote({ ...note, [name]: value });
};
const editNote = async (e) => {
e.preventDefault();
try {
const token = localStorage.getItem("tokenStore");
if (token) {
const { title, content, date, id } = note;
const newNote = {
title,
content,
date,
};
await axios.post(`api/notes/${id}`, newNote, {
headers: { Authorization: token },
});
return navigate.push("/");
}
} catch (err) {
window.location.href = "/";
}
};
return (
<>
<h2>Crear nota</h2>;
<form on onSubmit={editNote} autoComplete="off">
<div>
<label htmlFor="title">Title</label>
<input
type="text"
value={note.title}
id="title"
name="title"
required
onChange={onChangeInput}
/>
</div>
<div>
<label htmlFor="content">Content</label>
<input
type="text"
value={note.content}
id="content"
name="content"
required
rows="10"
onChange={onChangeInput}
/>
</div>
<div>
<label htmlFor="date">Date: {note.date}</label>
<input
type="date"
id="date"
name="date"
required
onChange={onChangeInput}
/>
</div>
<button type="submit">Guardar</button>
</form>
</>
);
}
It is my first post, if I have done something wrong, sorry and let me know.
Thanks in advance.
From your code it seems match is a props.
instead of accessing it like this:
export default function EditarNota(match)
try spreading the props like this:
export default function EditarNota({match})
or this way:export default function EditarNota(props)
then where ever you have match change it to props.match.
Instead of navigate.push('/'), navigate('/')
Trying my hands at ReactJS fetch() examples. As mentioned this.props.history.push() not working, it is not giving an error, but it is simply not redirecting. Tried to read other answers to this question on StackOverflow, but many of them are either too complex(some ppl showing off) or some not very clear.
index.js:
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import { BrowserRouter as Router, NavLink, Switch, Route, useHistory } from "react-router-dom";
import reportWebVitals from "./reportWebVitals";
ReactDOM.render(
<React.StrictMode>
<Router>
<App />
</Router>
</React.StrictMode>,
document.getElementById("root")
);
App.js :
import "./App.css";
import React from "react";
import { BrowserRouter as Router, NavLink, Switch, Route, useHistory } from "react-router-dom";
import RouterPage1 from "./RouterPage1";
import RouterPage2 from "./RouterPage2";
export default function App(props) {
let history = useHistory();
return (
<div>
<nav>
<ol>
<li>
<NavLink to="/RouterPage1">RouterPage1</NavLink>
</li>
<li>
<NavLink to="/RouterPage2">RouterPage2</NavLink>
</li>
<li>
<NavLink to="/RouterPageBoth">RouterPageBoth</NavLink>
</li>
</ol>
</nav>
<Switch>
<Route exact path="/RouterPage1">
<RouterPage1 history={history} />
</Route>
<Route exact path="/RouterPage2">
<RouterPage2 history={history} />
</Route>
</Switch>
</div>
);
}
RouterPage2.js(only the necessary code pasted, not the entire component for brevity):
addastakeholder = () => {
let newstakeholder = JSON.stringify(this.state.newstakeholder);
fetch("http://localhost:8080/OneToOneMappingPractice/add", {
method: "POST",
headers: { "Content-type": "application/json" },
body: newstakeholder,
}).then((r) => {
if (r.ok) {
//window.location.href = "/RouterPage2";
this.setState({ newstakeholder: { name: "", address: { house_number: "", streetName: "" } } });
this.props.history.push("/RouterPage2");
}
});
};
when I use the addastakeholder(), it is POSTing successfully, data is being entered in the DB, but it is not giving me an error and not redirecting. In the App.js, if I use the props and not useHistory(), it gives me "this.props.history not defined" error, even though I have enclosed App component in BrowserRouter component in the index.js.
Why is it so?
Secondly, if I use the commented out window.location.href = "/RouterPage2", it works(POSTing is successfull), but I am not able to see POST log in Development Tools:Network tab(Firefox). Why is this so?
Tried this.context.history.push("/RouterPage2"), does not work, same undefined error.
P.S.:edit 1:
the full RouterPage2.js(Kindly ignore result variable and the related code. Consider only result2.):
import React from "react";
export default class RouterPage2 extends React.Component {
constructor(props) {
super(props);
this.state = {
stakeholders: [],
errorString: "",
newstakeholder: { name: "", address: { house_number: "", streetName: "" } },
};
}
componentDidMount() {
fetch("http://localhost:8080/OneToOneMappingPractice/getAll")
.catch((error) => this.setState({ errorString: error }))
.then((result) => result.json())
.then((result) => this.setState({ stakeholders: result }));
}
addastakeholder = () => {
let newstakeholder = JSON.stringify(this.state.newstakeholder);
fetch("http://localhost:8080/OneToOneMappingPractice/add", {
method: "POST",
headers: { "Content-type": "application/json" },
body: newstakeholder,
}).then((r) => {
if (r.ok) {
//window.location.href = "/RouterPage2";
this.setState({ newstakeholder: { name: "", address: { house_number: "", streetName: "" } } });
this.props.history.push("/RouterPage2");
}
});
};
render() {
let result, result2;
let error = false;
if (this.state.stakeholders.length > 0)
result = (
<ol>
{this.state.stakeholders.map((stakeholder) => (
<li key={stakeholder.stakeholder_id}>
{stakeholder.stakeholder_name} | {stakeholder.stakeholder_type} |
{stakeholder.stakeholder_email_id} | {stakeholder.stakeholder_contactno} |
{stakeholder.stakeholder_bankname} | {stakeholder.stakeholder_bankBranch} |
{stakeholder.stakeholder_IFSC} | {stakeholder.stakeholder_AccNo} |
{stakeholder.stakeholder_AccType} | {stakeholder.stakeholder_PAN}
</li>
))}
</ol>
);
else result = false;
if (this.state.stakeholders.length > 0)
result2 = (
<ol>
{this.state.stakeholders.map((stakeholder) => (
<li key={stakeholder.id}>
{stakeholder.name}|{stakeholder.address.house_number}|{stakeholder.address.streetName}
</li>
))}
</ol>
);
else result2 = false;
if (this.state.errorString !== "undefined") error = this.state.errorString;
let blank = false;
if (result == false) blank = <h5>There are no records to display.</h5>;
return (
<div>
<h1>Stakeholder details :</h1>
{result2}
{error}
{blank}
<form>
Name :{" "}
<input
type="text"
placeholder="Name"
value={this.state.newstakeholder.name}
onChange={(e) => {
this.setState({ newstakeholder: { ...this.state.newstakeholder, name: e.target.value } });
}}
/>
<br></br>
StreetName :{" "}
<input
type="text"
placeholder="StreetName"
value={this.state.newstakeholder.address.streetName}
onChange={(e) => {
this.setState({
newstakeholder: {
...this.state.newstakeholder,
address: { ...this.state.newstakeholder.address, streetName: e.target.value },
},
});
}}
/>
<br></br>
HouseNumber :{" "}
<input
type="text"
placeholder="HouseNumber(Digits Only)"
value={this.state.newstakeholder.address.house_number}
onChange={(e) => {
this.setState({
newstakeholder: {
...this.state.newstakeholder,
address: { ...this.state.newstakeholder.address, house_number: e.target.value },
},
});
}}
/>
<br></br>
<button type="button" onClick={this.addastakeholder}>
Add Stakeholder
</button>
</form>
</div>
);
}
}
Tried everything. As suggested by Kurtis above in the comments, for troubleshooting purposes, did : console.log(this.props);
Got following response :
as you can see, there is the push function with different signature, hence tried : this.props.history.push("/RouterPage2", ""). Again did not work.
Hence, thought of trying the go(). And it worked.
this.props.history.go("/RouterPage2");
working now perfectly.
So I have built a Wizard Form using React-Final-Form that I am using in my sign-up page. I am trying to figure out how I can display all user inputs on the final page as a way for the user to double-check/verify their inputs before submitting. Any help would be greatly appreciated!
(P.S. - I tried researching this before posting, but all I was able to find was storing user inputs in Redux and accessing them from there, which I'd like to avoid, if at all possible.)
Here is an example link that shows what I want to do - Please feel free to fork and play around with it if you are trying to figure out a solution! https://codesandbox.io/s/0332k02x0v
Here is the code, shortened to include only the relevant bits:
My Wizard.js page:
import React, { Component } from "react";
import PropTypes from "prop-types";
import { Form } from "react-final-form";
class Wizard extends Component {
static propTypes = {
onSubmit: PropTypes.func.isRequired
};
static Page = ({ children }) => children;
constructor(props) {
super(props);
this.state = {
page: 0,
values: props.initialValues || {}
};
}
next = values =>
this.setState(state => ({
page: Math.min(state.page + 1, this.props.children.length - 1),
values
}));
previous = () =>
this.setState(state => ({
page: Math.max(state.page - 1, 0)
}));
validate = values => {
const activePage = React.Children.toArray(this.props.children)[
this.state.page
];
return activePage.props.validate ? activePage.props.validate(values) : {};
};
handleSubmit = values => {
const { children, onSubmit } = this.props;
const { page } = this.state;
const isLastPage = page === React.Children.count(children) - 1;
if (isLastPage) {
return onSubmit(values);
} else {
this.next(values);
}
};
render() {
const { children } = this.props;
const { page, values } = this.state;
const activePage = React.Children.toArray(children)[page];
const isLastPage = page === React.Children.count(children) - 1;
return (
<Form
initialValues={values}
validate={this.validate}
onSubmit={this.handleSubmit}
>
{({ handleSubmit, submitting, values }) => (
<form onSubmit={handleSubmit}>
{activePage}
<div className="buttons">
{page > 0 && (
<button type="button" onClick={this.previous}>
« Previous
</button>
)}
{!isLastPage && <button type="submit">Next »</button>}
{isLastPage && (
<button type="submit" disabled={submitting}>
Submit
</button>
)}
</div>
{/* <pre>{JSON.stringify(values, 0, 2)}</pre> */}
</form>
)}
</Form>
);
}
}
export default Wizard;
My index.js page:
import React, { Component } from "react";
import { Field } from "react-final-form";
import formatString from "format-string-by-pattern";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import Wizard from "./Wizard";
import Styles from "./Styles";
import { addUser } from "../../../actions/authActions";
class ReactFinalForm2 extends Component {
state = {};
render() {
const onSubmit = async values => {
this.props.addUser(values);
// API query here
};
const Error = ({ name }) => (
// Error handing here
);
return (
<Styles>
<div>
<Wizard initialValues={{}} onSubmit={onSubmit}>
<Wizard.Page
validate={values => {
// Page validation here
}}
>
// Page inputs here
</Wizard.Page>
<Wizard.Page
validate={values => {
// Page validation here
}}
>
// Page inputs here
</Wizard.Page>
<Wizard.Page
validate={values => {
// Page validation here
}}
>
// Page inputs here
</Wizard.Page>
<Wizard.Page>
{/* *** THIS IS WHERE I WOULD LIKE TO DISPLAY ALL PREVIOUS VALUES (SO THE USER CAN CONFIRM / DOUBLE-CHECK THEIR INPUTS BEFORE SUBMITTING) *** */}
</Wizard.Page>
</Wizard>
</div>
</Styles>
);
}
}
ReactFinalForm2.propTypes = {
addUser: PropTypes.func.isRequired
};
export default connect(
null,
{ addUser }
)(ReactFinalForm2);
I have added a state to the parent component. Changing this state on every submit from the child. I have JSON stringify the state in parent component. As you said no need to use redux, this is the workaround I came with. Still your code has a potential for improvements. Please check this working sandbox:
[ https://codesandbox.io/s/zrvloq4o6x ]
Wizard.js change
handleSubmit = values => {
const { children, onSubmit } = this.props;
const { page } = this.state;
const isLastPage = page === React.Children.count(children) - 1;
if (isLastPage) {
return onSubmit(values);
} else {
this.next(values);
}
// Change added
this.props.onStateChange(values);
};
Index.js
import React from "react";
import { render } from "react-dom";
import Styles from "./Styles";
import { Field } from "react-final-form";
import Wizard from "./Wizard";
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
const onSubmit = async values => {
await sleep(300);
window.alert(JSON.stringify(values, 0, 2));
};
const Error = ({ name }) => (
<Field
name={name}
subscribe={{ touched: true, error: true }}
render={({ meta: { touched, error } }) =>
touched && error ? <span>{error}</span> : null
}
/>
);
const required = value => (value ? undefined : "Required");
let data = {};
class App extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.onStateChange = this.onStateChange.bind(this);
}
onStateChange = values => {
this.setState({ data: values });
console.log(values);
};
render() {
return (
<Styles>
<h1>🏁 React Final Form Example</h1>
<h2>Wizard Form</h2>
<a href="https://github.com/erikras/react-final-form#-react-final-form">
Read Docs
</a>
<p>
Notice the mixture of field-level and record-level (or{" "}
<em>page-level</em> in this case) validation.
</p>
<Wizard
initialValues={{}}
onStateChange={this.onStateChange}
onSubmit={onSubmit}
>
<Wizard.Page>
<div>
<label>First Name</label>
<Field
name="firstName"
component="input"
type="text"
placeholder="First Name"
validate={required}
/>
<Error name="firstName" />
</div>
<div>
<label>Last Name</label>
<Field
name="lastName"
component="input"
type="text"
placeholder="Last Name"
validate={required}
/>
<Error name="lastName" />
</div>
</Wizard.Page>
<Wizard.Page
validate={values => {
const errors = {};
if (!values.notes) {
errors.notes = "Required";
}
return errors;
}}
>
<div>
<label>Best Stooge?</label>
<div>
<label>
<Field
name="stooge"
component="input"
type="radio"
value="larry"
/>{" "}
Larry
</label>
<label>
<Field
name="stooge"
component="input"
type="radio"
value="moe"
/>{" "}
Moe
</label>
<label>
<Field
name="stooge"
component="input"
type="radio"
value="curly"
/>{" "}
Curly
</label>
</div>
</div>
<div>
<label>Notes</label>
<Field name="notes" component="textarea" placeholder="Notes" />
<Error name="notes" />
</div>
</Wizard.Page>
<Wizard.Page>
<div>
<p>
<b>Display all previous values here for user verification </b>
<br />
<i>{JSON.stringify(this.state.data, 0, 2)}</i>
</p>
</div>
</Wizard.Page>
</Wizard>
</Styles>
);
}
}
render(<App />, document.getElementById("root"));
My problem is simple, at least it seems. I have state in my redux store that's holding the state of whether the user is logged in or not. That is all working fine, but when the user refreshes the page, for a moment while the authenticated state is async getting it's data, the render runs and state is undefined.
Since, state is undefined, my redirect to /login runs, so the refresh kicks me out of the app and back to login, which then checks to see if I'm already logged in and take me to the homepage.
Any ideas on how to resolve this:
{
!this.props.authenticated && (
<Switch>
<Route path="/login" component={LoginForm} />
<Route path="/register" component={RegisterForm} />
<Route path="" render={props => {
return <Redirect to="/login" />
}}
/>
</Switch>
)
}
So, when this.props.authenticated is false for that short period of time, it hits the login redirect. But, a few ms later, this.props.authenticated is true and since the user is already logged in, is redirected to the home route.
Any ideas?
Ideally you wouldn't render your route straight away but wait until your authentication request has resolved and you have a clear state.
Something like this:
class App extends React.Component {
constructor( props ) {
super( props );
this.state = {
// You could just check if authenticated is null,
// but I think having an extra state makes is more clear
isLoading: true,
authenticated: null,
};
this.checkAuthentication();
}
checkAuthentication() {
// Some async stuff checking against server
// I’ll simulate an async call with this setTimeout
setTimeout(
() => this.setState( {
authenticated: Boolean( Math.round( Math.random() ) ),
isLoading: false,
} ),
1000
);
}
render() {
// Render a loading screen when we don't have a clear state yet
if ( this.state.isLoading ) {
return <div>loading</div>;
}
// Otherwise it is safe to render our routes
return (
<div>
routes,<br />
random authenticated:
<strong>
{ this.state.authenticated.toString() }
</strong>
</div>
);
}
}
ReactDOM.render( (
<App />
), document.querySelector( 'main' ) );
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<main></main>
First of all when user try to login then you receive token in response when user is authenticated. now you have to store token in localStorage using
if(user.token){
localStorage.setItem('user', JSON.stringify(user));
}
it is indicated that when you have token in localstorage you are login otherwise you are logout.
now try to set state to redirect to home page if you want to go home page after login.
this.setState({redirectToReferrer: true});
now return redirect to desire page
if (this.state.redirectToReferrer){
return (<Redirect to={'/home'}/>)
}
login.js
import React from 'react';
import axios from 'axios';
import {Redirect} from 'react-router-dom';
export default class Login extends React.Component{
constructor(props){
super(props);
this.state = {
email : '' ,
password : '',
redirectToReferrer : false
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event){
this.setState({
[event.target.name] : event.target.value
});
}
handleSubmit(event){
event.preventDefault();
const user = {
email : this.state.email,
password : this.state.password
};
if(this.state.email && this.state.password)
{
axios.post(`{Api}/login`,user)
.then((response) =>
{
let userresponse = response;
console.log(userresponse.data);
if(userresponse.token){
sessionStorage.setItem('data',JSON.stringify(userresponse));
this.setState({redirectToReferrer: true});
}
},this)
.catch((error) => alert(error))
}
}
render(){
if (this.state.redirectToReferrer){
return (<Redirect to={'/user'}/>)
}
if (sessionStorage.getItem('data')){
return (<Redirect to={'/user'}/>)
}
return(
<div>
<form ref="formdemo" onSubmit={this.handleSubmit}>
<label>
Username:
<input type="email" name="email" onChange={this.handleChange} placeholder="Enter Your EmailID" required/></label><br/>
<label>
Password :
<input type="password" name="password" onChange={this.handleChange} placeholder="Enter Your Password" required/></label><br/>
<input type="submit"/>
</form>
</div>
)
}
}
Okay, lumio helped get me on the right track with setTimeout, so instead I worked it out with async/await:
class App extends Component {
state = {
error: "",
isLoading: true,
}
async componentDidMount() {
let token = localStorage.getItem('jwtToken');
if (token) {
setAuthToken(token);
await this.props.isAuthenticatedAction(true);
} else {
await this.props.isAuthenticatedAction(false);
}
this.setState({
isLoading: false,
});
}
handleLogout = (evt) => {
evt.preventDefault();
localStorage.removeItem('jwtToken');
window.location.reload();
}
render() {
if (this.state.isLoading) {
return <div></div>;
} else {
// return my regular content
}
You can use react-router-dom for auth work flow.
import React from "react";
import {
BrowserRouter as Router,
Route,
Link,
Redirect,
withRouter
} from "react-router-dom";
////////////////////////////////////////////////////////////
// 1. Click the public page
// 2. Click the protected page
// 3. Log in
// 4. Click the back button, note the URL each time
const AuthExample = () => (
<Router>
<div>
<AuthButton />
<ul>
<li>
<Link to="/public">Public Page</Link>
</li>
<li>
<Link to="/protected">Protected Page</Link>
</li>
</ul>
<Route path="/public" component={Public} />
<Route path="/login" component={Login} />
<PrivateRoute path="/protected" component={Protected} />
</div>
</Router>
);
const fakeAuth = {
isAuthenticated: false,
authenticate(cb) {
this.isAuthenticated = true;
setTimeout(cb, 100); // fake async
},
signout(cb) {
this.isAuthenticated = false;
setTimeout(cb, 100);
}
};
const AuthButton = withRouter(
({ history }) =>
fakeAuth.isAuthenticated ? (
<p>
Welcome!{" "}
<button
onClick={() => {
fakeAuth.signout(() => history.push("/"));
}}
>
Sign out
</button>
</p>
) : (
<p>You are not logged in.</p>
)
);
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route
{...rest}
render={props =>
fakeAuth.isAuthenticated ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: "/login",
state: { from: props.location }
}}
/>
)
}
/>
);
const Public = () => <h3>Public</h3>;
const Protected = () => <h3>Protected</h3>;
class Login extends React.Component {
state = {
redirectToReferrer: false
};
login = () => {
fakeAuth.authenticate(() => {
this.setState({ redirectToReferrer: true });
});
};
render() {
const { from } = this.props.location.state || { from: { pathname: "/" } };
const { redirectToReferrer } = this.state;
if (redirectToReferrer) {
return <Redirect to={from} />;
}
return (
<div>
<p>You must log in to view the page at {from.pathname}</p>
<button onClick={this.login}>Log in</button>
</div>
);
}
}
export default AuthExample;
refer link https://reacttraining.com/react-router/web/example/auth-workflow