I might be misunderstanding React, in which case I welcome suggestions to rearchitect my site.
My desired behaviour is after submitting the SimulationForm the user is then forwarded to another component called SimulationResults. This will take an id used to send a database query.
The current behaviour is that the simulation doesn't forward at all after submitting, staying on the same page.
Ideally, so that the user can easily return to the results in the future I would like it if the simulation id could be passed as a url path parameter to the results page, intead of going through the submit form again.
I have an index.js that looks like this:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import SimulationForm from './simulation_form'
import * as serviceWorker from './serviceWorker';
ReactDOM.render(<SimulationForm />, document.getElementById('root'));
serviceWorker.unregister();
And simulation_form.js like this:
import React from 'react';
export default class SimulationForm extends React.Component {
constructor(props) {
super(props);
this.state = {
simulationName: "",
...
...
};
this.handleSubmit = this.handleSubmit.bind(this);
...
}
...
...
handleSubmit(event) {
event.preventDefault();
...
...
}
...
...
return(
<form id="simulationForm" className="centered" onSubmit={this.handleSubmit}>
<label>Simulation name:</label>
<input type="text" placeholder="enter name here" name="simulationName" value={this.state.simulationName} onChange={this.handleChange} />
...
...
);
}
}
At the end of the handleSubmit function I've tried:
return <SimulationResults />;
and
ReactDOM.render(<SimulationResults />, document.getElementById('root'));
to no success. It's clear I don't understand how React wants me to set this up. I've been reading the docs and the web but can't find any examples of what I want to achieve. I'm also unsure as to how I pass state (an id string) to the results component from the form component.
simualation_results.js:
import React from 'react';
export default class SimulationResults extends React.Component {
constructor(props) {
super(props);
this.state = {
simId: ""
}
}
...
...
render() {
return <form id="simulationResults" className="centered" onSubmit={this.queryDatabase}>
<input type="text" placeholder="enter simId" name="simId" value={this.state.simId} onChange={this.handleChange} />
<input type="submit" value="Submit" />
</form>
}
#i.brod pointed me in the direction of react-router to achieve what I wanted.
npm install react-router-dom
I then added a <Router> to my index.js:
import {
BrowserRouter as Router,
Switch,
Route,
useParams
} from "react-router-dom";
ReactDOM.render(<Index />, document.getElementById('root'));
export default function Index() {
return (
<Router>
<div>
<Switch>
<Route exact path="/">
<SimulationForm />
</Route>
<Route path="/results/:id?">
<Results />
</Route>
</Switch>
</div>
</Router>
);
}
Where path="/results/:id?" matches /results as well as /results/{anything}.
I made my SimulationResults component a function instead of a standalone component.
function Results() {
let { id } = useParams();
queryDatabase(id);
return (
<div className="centered" >
<h3 id="results-header">waiting...</h3>
<form id="simulationResults" onSubmit={(e) => {e.preventDefault(); queryDatabase(document.getElementById("simHash").value);}}>
<input id="simHash" type="text" placeholder="enter simHash" />
<input type="submit" value="Submit" />
</form>
</div>
)
}
SimulationForm is still a component:
export default class SimulationForm extends React.Component {
...
}
To redirect from / to /results:id at the end of the the form's onSubmit I added:
window.location.href = '/results/' + id
Related
I have a Smart component class page PhoneDirectory.js, where I have used BrowserRouter and Route to route to the ShowSubscribers page("/") and AddSubscribers page ("/add"). My requirement is to redirect to the ShowSubscribers page("/") upon form submission on the AddSubscribers page but not understand how to implement that. I tried using this.props.history.push("/") but it isn't working. I am new to React, can anyone please help?
import React from 'react';
import AddSubscriber from './AddSubscriber';
import ShowSubscribers from './ShowSubscribers';
import {BrowserRouter , Route, Routes} from 'react-router-dom';
export default class PhoneDirectory extends React.Component{
constructor(){
super();
this.state = {[]};
}
addSubscribers = (subscribers) =>{...}
deleteSubscribers = (subscriberId) =>{...}
render() {
return (
<BrowserRouter>
<Routes>
<Route exact path='/' element={<ShowSubscribers deleteSubscribers={this.deleteSubscribers} subscribersList={this.state.subscribersList}/>}/>
<Route exact path='/add' element={<AddSubscriber addSubscribers={this.addSubscribers}/>}/>
</Routes>
</BrowserRouter>
)
}
}
Dumb component AddSubscriber page
import React from 'react';
import Header from './Header';
import './AddSubscriber.css';
import {Link} from "react-router-dom";
export default class AddSubscriber extends React.Component {
constructor() {
super();
this.state = {
id: 0,
name:'',
phone:''
}
}
onChangeHandler = (event) =>{...}
onFormSubmitted = (event) =>{
event.preventDefault();
this.props.addSubscribers(this.state);
this.setState({id:0, name:"", phone:''});
// Need logic to redirect to "/" i.e., ShowSubscribers page
}
render() {
return (<div>
<Header heading="Add Subscriber"/>
<div className="component-body-container">
<Link to="/">
<button className="custom-btn">Back</button>
</Link>
<form className="subscriber-form" onSubmit={this.onFormSubmitted.bind(this)}>
...............
<button type="submit" className="custom-btn add-btn">Add</button>
</div>
</form>
</div>
</div>)}
}
I tried this.props.history.push("/"), but it didn't worked.
react-router-dom v6 support only hooks version i suggest since you use class component to downgrade the version of react-router-dom in package.json.
"dependencies": {
...
"react-router-dom": "5.2.1",
},
Now you need to run : npm install or yarn install
Your Route component will look like that
import React from 'react';
import AddSubscriber from './AddSubscriber';
import ShowSubscribers from './ShowSubscribers';
import { Route, Switch, BrowserRouter as Router } from 'react-router-dom';
export default class PhoneDirectory extends React.Component{
constructor(){
super();
this.state = {[]};
}
addSubscribers = (subscribers) =>{...}
deleteSubscribers = (subscriberId) =>{...}
render() {
return (
<Router>
<Switch>
<Route exact path='/'>
<ShowSubscribers {...your props goes here}/>
</Route>
<Route exact path='/add'>
<AddSubscriber {...your props goes here}/>
</Route>
</Switch>
</Router>
)
}
}
Now you are able to call this.props.history.push
import React from 'react';
import Header from './Header';
import './AddSubscriber.css';
export default class AddSubscriber extends React.Component {
constructor() {
super();
this.state = {
id: 0,
name:'',
phone:''
}
}
onChangeHandler = (event) =>{...}
onFormSubmitted = (event) =>{
event.preventDefault();
this.props.addSubscribers(this.state);
this.setState({id:0, name:"", phone:''});
// Need logic to redirect to "/" i.e., ShowSubscribers page
}
render() {
return (<div>
<Header heading="Add Subscriber"/>
<div className="component-body-container">
<button className="custom-btn" onClick={()=>this.props.history.push('/')} >Back</button>
<form className="subscriber-form" onSubmit={this.onFormSubmitted.bind(this)}>
...............
<button type="submit" className="custom-btn add-btn">Add</button>
</div>
</form>
</div>
</div>)}
}
I think that you need to wrap your AddSubscriber page with withRouter HOC
class AddSubscriber extends React.Component{
...some-code....
}
export default withRouter(AddSubscriber)
you need navigation logic (this.props.history.push("/"))
to be located a addScubscribers page.
(you have it at "homepage" instead)
I'm building a website using React and I'm trying to redirect the user to the index page after the login, but my component is not rendering anything, although I'm being redirected and I can see the URL changing from /welcome#/login to /main.
Since I'm not getting any error messages and the webpack is being successfully compiled, I can't see what's wrong anymore.
Any ideas of what could possibly be wrong?
Thank you very much!
Start.js
import React from "react";
import ReactDOM from "react-dom";
import Welcome from "./welcome";
import { App } from "./app";
import { Provider } from "react-redux";
import { createStore, applyMiddleware } from "redux";
import reduxPromise from "redux-promise";
import { composeWithDevTools } from "redux-devtools-extension";
import reducer from "./reducer";
const store = createStore(
reducer,
composeWithDevTools(applyMiddleware(reduxPromise))
);
let element;
if (location.pathname === "/welcome") {
element = <Welcome />;
} else {
init(store);
element = (
<Provider store={store}>
<App />
</Provider>
);
}
ReactDOM.render(element, document.querySelector("main"));
Welcome.js
import React from "react";
import Register from "./register";
import Login from "./login";
import { HashRouter, Route, Redirect } from "react-router-dom";
export default class Welcome extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<HashRouter>
<div className="register-wrapper">
<div>
<Route path="/login" component={Login} />
<Route exact path="/" component={Register} />
</div>
</div>
</HashRouter>
);
}
}
Login Component
import React from "react";
import axios from "./axios";
export default class Login extends React.Component {
constructor(props) {
super(props);
this.state = { error: false };
this.loginButton = this.loginButton.bind(this);
this.handleChange = this.handleChange.bind(this);
}
loginButton(e) {
e.preventDefault();
axios
.post("/login", this.state)
.then(res => {
if (res.data.success) {
location.replace("/main");
} else {
this.setState({
error: true
});
}
})
.catch(error => console.log(error));
}
handleChange(e) {
this.setState(
{
[e.target.name]: e.target.value
},
() => console.log("this.state:", this.state)
);
}
render() {
return (
<div className="login-main-container">
{this.state.error && <h2>Ops! Something went wrong.</h2>}
<h1 className="login-title">Kathi & Rodolfo</h1>
<h2 className="login-subtitle">Dear guest, please login first</h2>
<div className="login-container">
<form className="login-form">
<label className="label-login" htmlFor="email"> username </label>
<input className="input-login"
name="email"
placeholder="Best Couple You Know"
onChange={this.handleChange}
/>
<label className="label-login" htmlFor="password"> password </label>
<input className="input-login"
name="password"
type="password"
placeholder="Super Loving Password"
onChange={this.handleChange}
/>
<button
className="login-button"
onClick={this.loginButton}
>
Login
</button>
</form>
</div>
<h4 className="login-info">Information about username and password can be found on the Save The Date card</h4>
</div>
);
}
}
Index Component (Main)
import React from "react";
export default class Main extends React.Component {
constructor(props) {
super(props);
this.state ={ error: false};
}
render () {
return (
<div className="main-container">
<header>
<p>the wedding</p>
<p>rpsv</p>
<p>contact us</p>
<p>wedding gift</p>
</header>
{this.state.error && <h2>Ops! Something went wrong.</h2>}
<div className="save-the-date-img">
<h1>Save The Date</h1>
</div>
</div>
);
}
}
App.js
import React from "react";
import { BrowserRouter, Route, Link } from "react-router-dom";
import Main from "./main";
export class App extends React.Component {
constructor() {
super();
this.state = {};
}
render() {
return (
<BrowserRouter>
<div>
<Route exact path="/main" Component={Main}/>
</div>
</BrowserRouter>
);
}
}
location.replace("/main");
I think this line is wrong in your code.
While you are using React, you'd rather use react-router-dom's functionality than browser built-in feature.
Change the line to this.props.history.push('/main')
Give your file structure. Your component Login is not in BrowserRouter, but must be. Check the official sample: https://reacttraining.com/react-router/web/guides/quick-start.
instead of
location.replace("/main");
use
history.push("/main")
I want to redirect to other page with search results after submitting form (search bar feature).
I will usually redirect with return <Redirect to={'/search/' + this.state.value} /> in render(), but I can't now since I need to render entire site (search bar is in main component).
I already tried this.props.history.push('/search/' + this.state.value) and this.context.router.history.push('/search/' + this.state.value) in place of comment but it doesn't work.
(Code simplified for clarity)
import React, { Component } from "react";
import {
Route,
NavLink,
BrowserRouter
} from "react-router-dom";
import Search from "./Search";
import Autosuggest from 'react-autosuggest';
class App extends Component {
constructor(props) {
super(props);
this.state = { value: '' };
this.search = this.search.bind(this);
}
// react-autosuggest code
search(e) {
e.preventDefault();
// Redirect to '/search/' + this.state.value
}
render() {
return (
<BrowserRouter>
<div className="app">
<div className="header">
<form className="search-form" onSubmit={this.search}>
{/* Autosuggest input which updates this.state.value */}
</form>
</div>
<div className="main">
<Route path="/search/:handle" component={Search} />
{/* Other Routes */}
</div>
</div>
</BrowserRouter>
);
}
}
export default App;
Both this.props.history and this.context.router.history are undefined if I try to use them. <Redirect /> (obviously) can't be rendered instead of <BrowserRouter />
Have you tried using withRouter HOC to App, giving you access to history as a prop?
class App extends Component {
// ...
search(e) {
e.preventDefault();
this.props.history.push(`/search/${this.state.value}`)
}
// ....
}
export default withRouter(App)
You need to import Redirect from react-router-dom,
import {Redirect} frpm 'react-router-dom'
And usage,
search(e) {
e.preventDefault();
if(this.state.value){
return <Redirect to={`/search/${this.state.value}`}
}
}
As I'm new to both react and typescript I've read several articles about how to get the "match" property from react-router working using typescript and I haven't been able to successfully do it. I have no parameters I am passing, I just want to be able to use the match.url of react-router.
So the examples I am seeing is that the field in the login.tsx file should use {match.url}. I tried the react 4 router demo here: https://codesandbox.io/s/nn8x24vm60?from-embed where they used {${match.url}/register} but that didn't work either. Then I saw some posts where people said you had to declare an interface but it was split between RouteComponent and Route and most were dealing with parameters. Basically all I want it to do is when I click the link to switch to the register route(it's in register.tsx - not shown here as it's a simple file with just a header on it).
Currently its throwing the following eror:
"Error TS2739 (TS) Type '{}' is missing the following properties from type
'Readonly<MyProps & RouteComponentProps<{}, StaticContext, any>>': register, history, location, match"
Any help as to what I'm doing wrong would be appreciated.
app.tsx file:
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, Redirect, Link, Route, Switch } from 'react-router-dom';
import { Home } from './home';
import { Register } from './register';
import { NavBar } from './navbar';
export class App extends React.Component<{}, {}> {
render() {
return (
<Router>
<div>
<NavBar/>
<Switch>
<Route path="/" component={Home} />
<Route path="/register" component={Register} />
</Switch>
</div>
</Router>
);
}
}
ReactDOM.render(, document.getElementById('root'));
home.tsx file:
import React from 'react';
import { Login } from './login';
import { Jumbotron } from 'react-bootstrap';
const jumboStyle = {
background: 'lightgray',
height: '20%',
width: '40%',
margin: 'auto'
};
export class Home extends React.Component<{}, {}> {
render() {
return (
<div>
< Jumbotron style={jumboStyle}>
<h1>Welcome to the new League!</h1>
<h4>Please log in with your username and password to continue</h4>
<Login />
<br />
<br />
</Jumbotron>
</div>
);
}
}
login.tsx file:
import React from 'react';
import { Link, RouteComponentProps } from "react-router-dom";
import TextField from '#material-ui/core/TextField';
import Button from '#material-ui/core/Button';
import { Alert } from 'react-bootstrap';
import { Register } from './register';
interface IState {
[key: string]: any; // or the type of your input
}
interface MyProps {
register: Register
}
const styles = {
background: 'lightblue'
};
export class Login extends React.Component<MyProps & RouteComponentProps, IState> {
constructor(props) {
super(props);
this.state = {
username: '',
password: '',
authorized: false
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
const target = event.target;
const value = target.value;
const name = target.name;
this.setState({
[name]:value
});
}
handleSubmit(event) {
//we are goingto submit the form to the database
event.prevent.default();
}
render() {
return (
<div>
<form noValidate autoComplete="off" onSubmit={this.handleSubmit} style={{ textAlign: 'center' }}>
<TextField
id="username"
name="username"
label="UserName"
helperText="Enter your Username"
value={this.state.username}
onChange={this.handleChange}
required={true}
style={styles}
/>
<br />
<TextField
id="password"
name="password"
type="password"
helperText="Enter your password"
label="Password"
onChange={this.handleChange}
required={true}
style={styles}
/>
<br />
<br />
<br/>
<Button
type="submit"
value="Submit"
variant="contained"
color="primary"
>Submit</Button>
<br />
<br/>
<Alert variant="info">
<Alert.Heading>Don't Have An Account Setup?</Alert.Heading>
<div>
<Link to={`${this.props.match.url}/register`}>Register Here</Link>
</div>
</Alert>
</form>
</div>
)
}
}
By default the react-router properties (such as match) are only automatically injected into the component rendered by a route (Home in this case). You can add them to your component using the withRouter wrapper. You then have to use the wrapped component instead of the internal one.
import {withRouter} from 'react-router';
...
class LoginInternal extends React.Component<MyProps & RouteComponentProps, IState> {
...
export const Login = withRouter(LoginInternal);
You need to pass props from Home component to Login component.
<Login match={this.props.match}/>
It should work.
I've seen many answer on this website and in official docs and many other places. But I couldn't able to achieve results. That's why I'm posting this question.
SO, my question is I have 3 files named:
App.js, SignUp.js and Welcome.js.
My task is to get value in form from SignUp.js page and print that value to the Welcome.js component. I read about lifting state up and higher order components but couldn't able to do it.
Any help is appreciated.
Here's my code:
App.js:
import React,{Component} from "react";
import {BrowserRouter as Router, Link, Switch, Route} from "react-router-dom";
import SignUp from './SignUp';
import Welcome from './Welcome';
class App extends Component {
render(){
var homeMessage = () =>{
return(<div><SignUp /></div>);
}
return(
<Router>
<div>
<Route exact path="/src/Welcome" component={Welcome}/>
<Route exact path="/" component={homeMessage}/>
</div>
</Router>
);
}
}
export default App;
SignUp.js;
import React,{Component} from "react";
import {Link} from "react-router-dom";
export default class SignUp extends Component {
constructor(props){
super(props);
this.state = {
firstName:''
};
}
inputData = event =>
{
this.setState({
[event.target.name]:event.target.value
});
}
submitData = event =>
{
event.preventDefault();
}
render(){
return(
<div>
<form onSubmit={this.submitData}>
<input type="text" name="firstName" onChange={this.inputData}/>
<button type="submit"> Submit</button>
</form>
<Link to="/src/Welcome">Welcome</Link>
</div>
);
}
}
Welcome.js:
import React,{Component} from "react";
import SignUp from './SignUp';
export default class Welcome extends Component {
render(){
return(
<div>
{this.state.firstName}
</div>
);
}
}
"And please give answer if possible then in react-way only not redux or any other library. Because as per Dan Abramov article "Thinking in React" so first I want to try all scope which is available in react only."
Pass the data back to the parent from SignUp component and then pass it as a prop to the Welcome component. The other way is to store the SignUp component state in the App component so that you don't need to pass it back up when you submit.
App.js
import React,{Component} from "react";
import {BrowserRouter as Router, Link, Switch, Route} from "react-router-dom";
import SignUp from './SignUp';
import Welcome from './Welcome';
class App extends Component {
state = {
firstName: ''
}
onSignIn = (value) => {
this.setState({firstName: value});
}
render(){
return(
<Router>
<div>
<Route exact path="/src/Welcome" render={(props) => <Welcome firstName={this.state.firstName} {...props}/>}/>
<Route exact path="/" render={(props) =>{
return(<div><SignUp onSignIn={this.onSignIn} {...props} /></div>);
}}/>
</div>
</Router>
);
}
}
export default App;
SignUp.js
export default class SignUp extends Component {
constructor(props){
super(props);
this.state = {
firstName:''
};
}
inputData = event =>
{
this.setState({
[event.target.name]:event.target.value
});
}
submitData = event =>
{
event.preventDefault();
this.props.onSignIn(this.state.firstName);
}
render(){
return(
<div>
<form onSubmit={this.submitData}>
<input type="text" name="firstName" onChange={this.inputData}/>
<button type="submit"> Submit</button>
</form>
<Link to="/src/Welcome">Welcome</Link>
</div>
);
}
}
Welcome.js
import React,{Component} from "react";
import SignUp from './SignUp';
export default class Welcome extends Component {
render(){
return(
<div>
{this.props.firstName}
</div>
);
}
}
Some useful links:
How to pass data from child component to its parent in ReactJS?
Passing custom props to router component in react-router v4
ReactJS - Lifting state up vs keeping a local state