PROBLEM
So I have two files in my auth folder one is called Signin and the other is called SignUp These two files are identical except for the names. all of the tags are the same the only difference is the naming of the file and the component.
For some reason though I am only ever able to see the SignIn component when the other component is created in the exact same way.
Here is the SignUp component, the one that is not being rendered.
import Axios from "axios";
import React, { useState, useContext } from "react";
import '../comStyles.css'
import UserContext from '../../context/UserContext';
import { useHistory } from 'react-router-dom';
export default function SignUp() {
const [email, setEmail] = useState();
const [password, setPassword] = useState();
const { setUserData } = useContext(UserContext);
const history = useHistory();
const submit = async (e) => {
e.preventDefault();
const existingUser = {email, password};
const loginRes = await Axios.post("http://localhost:8174/user/login", existingUser);
setUserData({
token: loginRes.data.token,
user: loginRes.data.token
});
localStorage.setItem("auth-token", loginRes.data.token);
history.push("/");
}
return (
// <div className="w-50 bg-black signinbox align-items-center justify-content-center h-50">
<div className="signinbox w-50 h-50">
<div className="shadow-lg bg-black p-3">
<h1 className="title my-2">UNT LMS</h1>
<form onSubmit={submit}>
<input id="login-email" className="form-control mr-sm-7 my-3" type="email" placeholder="Username" aria-label="Search" onChange={(e) => setEmail(e.target.value)}/>
<input className="form-control mr-sm-7 shadow-lg bg-black rounded" type="password" placeholder="Password" aria-label="Search" onChange={(e) => setPassword(e.target.value)}/>
<button className="bg-black signinbutton btn btn-outline-success my-4" type="submit">Sign Up</button>
</form>
</div>
</div>
);
}
It is being rendered by another component called AuthLinks like this. (Same way for SignIn)
<Link to="/SignUp" className="nav-link">
Sign Up
</Link>
This creates a link that can take you to /SignUp component or page.
However as it does in fact change the url to /SignUp there is nothing to show on the page. The component is failing to render even though if I go to /SignIn it renders to the browser fine.
Here are how the routes are setup in App.js
import { NavBar } from './components';
import SignIn from './components/auth/SignIn';
import SignUp from './components/auth/SignUp';
<Switch>
<Route path="/Books/list" exact component={BooksList} />
<Route path="/Books/create" exact component={BooksInsert} />
<Route path="/Books/update/:id" exact component={BooksUpdate} />
<div className="vh-100 align-items-center justify-content-center">
<Route path="/SignIn" exact component={SignIn} />
</div>
<div className="vh-100 align-items-center justify-content-center">
<Route path="/SignUp" exact component={SignUp} />
</div>
</Switch>
Trouble Shooting
I have read other questions about the component and their capitalization. I don't think that this is the case for this question. As I have tried creating a whole new file called Register and changing everything to /Register.
I tried using Lorem ipsum in case the text or anything was just hiding behind the navbar.
I tried making it the full width and height of the screen the VH as well.
tried to refresh the page.
restarted the server on port 3000
Related
Ive made a search and filtering bar as part of an application im making in React. The current way it works is suggestions appear as you type. However there is no handler for if the user just clicks the submit button. At the moment clicking the submit button will take you to a results page with the query in the page URL.
I would like this to be passed as a state when you click the link. This link could then be displayed in the Results component.
Ive attempted this but im fairly new to React so any help would be appreciated.
Heres the search component:
import * as React from 'react';
import { useState } from "react";
import { Link } from "react-router-dom";
const content = [
{link: '/review/elden-ring', name: 'Elden\nRing'},
{link: '/review/', name: 'defg'},
{link: '/review/', name: 'ghij'},
{link: '/review/', name: 'jklm'},
]
export default function Search(props) {
//For storing and setting search input
const [query, setQuery] = useState("");
return (
//Search input
<div class="flex flex-col z-10">
<form class="text-black ml-5 py-0.5 lg:py-0 flex border-2 border-gray-400 rounded-md bg-white px-1">
<input id="searchInput" class="focus:outline-none" type="text" placeholder="Search" value={query} onChange={event => {setQuery(event.target.value)}}/>
<div class="flex mt-1.5"> {/* Flex container to align the icon and bar */}
<Link to={{pathname: "/results/" + query, state: {query}}}> {/* Error handler as search is strick */}
<button type="submit" onClick={() => setQuery(() => "")}>
<svg class="fill-current h-auto w-4 " xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"> {/* ! Font Awesome Pro 6.1.0 by #fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. */}
<path d="M500.3 443.7l-119.7-119.7c27.22-40.41 40.65-90.9 33.46-144.7C401.8 87.79 326.8 13.32 235.2 1.723C99.01-15.51-15.51 99.01 1.724 235.2c11.6 91.64 86.08 166.7 177.6 178.9c53.8 7.189 104.3-6.236 144.7-33.46l119.7 119.7c15.62 15.62 40.95 15.62 56.57 0C515.9 484.7 515.9 459.3 500.3 443.7zM79.1 208c0-70.58 57.42-128 128-128s128 57.42 128 128c0 70.58-57.42 128-128 128S79.1 278.6 79.1 208z" />
</svg>
</button>
</Link>
</div>
</form>
{/* Search Suggestions */}
<div class="ml-5 px-0.5">
{/* Query must have length to prevent mapping by default */}
{query.length > 0 && content.filter((content) => {
//If input return object
if (query == "") {
return content
}
//If any input characters much object characters return corresponding object
else if (content.name.toLowerCase().includes(query.toLocaleLowerCase())) {
return content
}
})
//Maps element based on the number of json objects
.map((content) => {
return(
<div class="bg-white rounded-sm">
<Link to={content.link} onClick={() => setQuery(() => "")}><p>{content.name}</p></Link>
</div>
);
})};
</div>
</div>
);
};
Heres the Results component
import * as React from 'react';
export default function Results(props) {
return (
<h1>{props.location.state.query}</h1>
);
};
Routes
import * as React from 'react';
import './app.css';
import { Routes, Route } from "react-router-dom";
import Header from './components/header/header';
import Footer from './components/footer';
import Error from './components/error';
import Results from './components/results';
import Index from './components/index/index';
import ReviewsPage from './components/reviews/reviewsPage';
import Review from './components/reviews/review';
export default function App() {
return (
<>
<Header />
<Routes>
<Route path="/" element={<Index />} />
<Route path="/reviews" element={<ReviewsPage />} />
{/* Render review with ID for switch statment */}
<Route path="/review/:id" element={<Review />} />
<Route path="/results/:id" element={<Results />} />
<Route path="*" element={<Error />} />
</Routes>
<Footer />
</>
);
};
Search component import line 30
import * as React from 'react';
import Search from './search';
import { useState } from 'react';
import { Link } from 'react-router-dom';
export default function Header() {
//State to toggle navlinks on small screens
const [state, setState] = useState(false)
return (
<nav className=" w-full bg-red-500 shadow-lg relative max-h-[4.1rem]"> {/* Max height set to avoid search suggestions increasing header size */}
<div className="flex justify-between py-3.5 w-full px-3 md:w-2/3 md:px-0 m-auto">
{/* Logo */}
<Link className="text-2xl font-semibold text-white hover:animate-pulse whitespace-nowrap" to="/">GAME REVIEWS</Link>
<div className="flex max-h-[3rem]"> {/* Container to prevent flex effecting both parents container */}
{/* Links */}
{!state && (
<ul id="links" className=" h-40 lg:h-auto flex-col flex lg:flex-row absolute lg:relative mt-10 lg:mt-0 right-0 lg:right-auto px-10 lg:px-0 bg-red-500 rounded-lg lg:rounded-none shadow-sm lg:shadow-none">
<li className="m-5 lg:my-0 lg:mx-5">
<Link className="text-2xl text-white border-none hover:border-solid border-b-2 border-white" to="/">Home</Link>
</li>
<li className="m-5 lg:my-0 lg:mx-5">
<Link className="text-2xl text-white border-none hover:border-solid border-b-2 border-white" to="/reviews">Reviews</Link>
</li>
</ul>
)}
{/* Search bar */}
<Search />
{/* Hamburger */}
<div id="hamburger" onClick={() => setState(!state)} className=" space-y-2 ml-5 mt-2 block cursor-pointer lg:hidden">
<div className="w-6 h-0.5 bg-white"></div>
<div className="w-6 h-0.5 bg-white"></div>
<div className="w-6 h-0.5 bg-white"></div>
</div>
</div>
</div>
</nav>
)
}
Heres an example of what I want to achieve
User searches 'game'
Upon clicking the icon on the right they should be redirected to my results page. This page should show what they just entered on submit.
You can use dynamic route in the Link component which passes query in the URL. And to parse it in Result component, you can use match props.
To navigate change your Link component to
<Link to={"/results/" + query} />
And to parse the query in Result component, use
<h1>{props.match.params.id}</>
If you want the page results to be shared, you must include on the url the search term something like: www.yourdomain.com/review/elden-ring
Take a look and you will see that I've defined that the review route now expects a parameter. You should use that parameter to check all the data you need to display on the page.
And had to edit the Search component because you're using class instead of className for styling.
On the Results component I use the useParams hook to get the url params and show it on the h1. You should use this param as a key to retrieve the actual details of the review of your API.
This is how I'd do (all the logic):
On the App component I define the routes:
<Routes>
<Route exact path="/" element={<Search />} />
<Route path="/review/:query" element={<Results />} />
</Routes>
On the Search component:
// Router v6 hook to navigate the user
const navigate = useNavigate();
const queryRef = useRef(null) // Reference to the input
// Navigates the user to reviews/what they've written
const queryHandler = () => navigate(`/reviews/${queryRef.current.value}`);
return (
<>
// This is where the user types the query
<input type='text' ref={queryRef} placeholder='Search' />
<Icon onClick={queryHandler} /> // This has the onClick to hndle the click
</>
)
And on the results component:
const params = useParams(); // This retrieves all the params defined for the url
<h1>{params.query}</h1>
The query you're sending in the history.push() method must be an object. Instead, you are sending a string. Change it to object like below
props.history.push({
pathname: '/results',
state: { query }
});
For the following route.
localhost:3000/search/?query=ABCD
The following code on load extract the ABCD from query=ABCD and set to the state.
export default function App() {
const [query, setQuery] = useState(() => {
const q = new URLSearchParams(window.location.search);
console.log(q.toString());
return q.get("query") || "";
});
return (
<div className="App">
<h1>Query</h1>
<h2>{query}</h2>
</div>
);
}
So this way you can extract the info from a route.
Now if you want to know how to move from one page to another
assuming you are using some routing library, look at how you can change the route
history.push(`/search/?query={query}`)
is a way to use with react router ( ensure you use the useHistory hook for it )
It was far simpler than I thought and something I had done on a different page
Creating a state for the input.
Setting input as the variable in that state (query).
Setting the value as the input using an onClick on the button.
The link then provided the state variable with the route.
const [query, setQuery] = useState("");
<form
className="text-black ml-5 py-0.5 lg:py-0 flex border-2 border-gray-400 rounded-md bg-white px-1"
>
<input
id="searchInput"
className="focus:outline-none"
type="text"
placeholder="Search"
value={query}
onChange={(event) => {
setQuery(event.target.value);
}}
/>
{/* Flex container to align the icon and bar */}
<div className="flex mt-1.5">
<Link to={{ pathname: "/results/" + query }}>
<button type="submit" onClick={() => setQuery(() => "")}>
<svg
className="fill-current h-auto w-4 "
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
{/* ! Font Awesome Pro 6.1.0 by #fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. */}
<path d="M500.3 443.7l-119.7-119.7c27.22-40.41 40.65-90.9 33.46-144.7C401.8 87.79 326.8 13.32 235.2 1.723C99.01-15.51-15.51 99.01 1.724 235.2c11.6 91.64 86.08 166.7 177.6 178.9c53.8 7.189 104.3-6.236 144.7-33.46l119.7 119.7c15.62 15.62 40.95 15.62 56.57 0C515.9 484.7 515.9 459.3 500.3 443.7zM79.1 208c0-70.58 57.42-128 128-128s128 57.42 128 128c0 70.58-57.42 128-128 128S79.1 278.6 79.1 208z" />
</svg>
</button>
</Link>
</div>
</form>
Then in the route the path is given a variable (id)
<Route path="/results/:id" element={<Results />} />
This could then be pulled in the results page by useParams.
And assigned to my h1 tag.
import { useParams } from "react-router-dom";
export default function Results(props) {
const {id} = useParams();
return (
<h1>{id}</h1>
);
};
Thank you for everyone help and guidance.
So, I'm new at React and I was making a WebApp using the API made by me that is full functional. I wanted to use token in sign in as the API is also prepared. Went to check a tutorial but it wont work because of the setToken.
I have no idea if is because of the fetch or anything else.
This is the error that appears when loading the page:
enter image description here
Then when I click the sign in button will always appear me this:
enter image description here
import React, { useContext, useState } from 'react';
import {BrowserRouter as Router, Routes, Route} from 'react-router-dom';
import './App.css';
import ListGlucoseRecords from './screens/glucoseRecords/listGlucoseRecords';
import ListInsulineRecords from './screens/insulineRecords/listInsulineRecords';
import DashboardComp from './screens/dashboard/dashboardComp';
import ClientList from './screens/users/clientList';
import Login from './screens/login/Login';
import SignUp from './screens/signUp/SignUp';
function App() {
const [token, setToken] = useState([]);
if(!token) {
return <Login setToken={setToken} />
}
return (
<Router>
<Routes>
<Route path='/dashboard' element={<DashboardComp/>}/>
<Route path='/users' element={<ClientList/>}/>
<Route path='/glucose' element={<ListGlucoseRecords/>}/>
<Route path='/insuline' element={<ListInsulineRecords/>}/>
<Route path='/' element={<Login/>}/>
<Route path='/signUp' element={<SignUp/>}/>
</Routes>
</Router>
);
}
export default App;
import React, { useState } from 'react';
import './Login.css';
import logo from '../../images/HealthControl.png';
import { Link } from 'react-router-dom';
import { URL } from '../../components/apiURL';
import axios from 'axios';
import PropTypes from 'prop-types';
async function loginUser(credentials) {
return fetch(`${URL}/user/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(credentials)
})
.then(data => data.json())
}
export default function Login({ setToken }) {
const [email, setEmail] = useState([]);
const [password, setPassword] = useState([]);
const handleSubmit = async e => {
e.preventDefault();
const token = await loginUser({
email,
password
});
setToken(token);
}
return (
<body>
<div class="Login">
<div class="columns is-vcentered">
<div class="login column is-4 ">
<section class="section">
<div class="has-text-centered">
<img class="login-logo has-background-primary" src={logo} alt="Logo" />
</div>
<form onSubmit={handleSubmit}>
<div class="field">
<label class="label">Username</label>
<div class="control has-icons-right">
<input class="input" type="text" onChange={e => setEmail(e.target.value)}></input>
<span class="icon is-small is-right">
<i class="fa fa-user"></i>
</span>
</div>
</div>
<div class="field">
<label class="label">Password</label>
<div class="control has-icons-right">
<input class="input" type="password" onChange={e => setPassword(e.target.value)}></input>
<span class="icon is-small is-right">
<i class="fa fa-key"></i>
</span>
</div>
</div>
<div class="has-text-centered">
<button type="submit" class="button is-vcentered is-primary is-outlined">Login</button>
</div>
</form>
<div class="has-text-centered">
<Link to='/signUp' className="nav-links">
<a> Don't you have an account? Sign up now!</a>
</Link>
</div>
</section>
</div>
<div id="particles-js" class="interactive-bg column is-8">
</div>
</div>
</div>
</body>
);
};
Login.propTypes = {
setToken: PropTypes.func.isRequired
};
It's not clear what you want to do exactly but I think there are a couple of issues in your code:
You're using useContext and passing in an empty array. This hook expects a context object created via React.createContext.
Also, the idea with context is that you wrap your components in a context provider so any children can access that context. This way they can access state without having to pass down properties.
But you're not using a context provider, you're passing down setToken anyway.
Login requires setToken, but on this line
<Route path='/' element={}/>
you're not passing it down. So you get errors because setToken is undefined inside Login and it is a required property.
What I think you want to do is:
Use useState instead of useContext. Don't define the initial value of token, so the if (!token) is true initially, and false after you call setToken.
const App = () => {
const [token, setToken] = useState();
if (!token) {
return <Login setToken={setToken} />;
}
return (
<Router>
<Routes>
<Route path="/login" element={<Login setToken={setToken} />} />{" "}
</Routes>
</Router>
);
};
Even this would be a bit weird because after logging in you'd show the login page again, as this is the default route. Probably not what you want to do.
I suggest you think well about the solution before you start coding.
This is caused because you have used useContext to create a state, you should instead use useState.
useState takes in initial state value returns stateful value and a function to update it.
useContext takes in Context returns value that is returned by Context.Provider
more about useContext
const [token, setToken] = useState(/* initial state value */);
EDIT: Events are not working at all, the onSubmit and onChange functions are not being called. I have another reactapp with similar form and onChange and onSubmit works OK there.
I have a form I dont want the page to refresh when I click on submit. I tried using preventDefault() but I didnt work. Even onChange is printing anything on console. This form is not on page, I am using React Router to point to='/new' and component={NewPost} (NewPost is in ./components/posts/form)
./components/posts/form.js:
import React, { Component } from "react";
import { connect } from "react-redux";
class NewPost extends Component {
state = {
title: "",
body: "",
status: 0,
};
onSubmit = (e) => {
e.preventDefault();
const post = e;
console.log(post);
};
onChange = (e) => {
console.log(e.target.value);
this.setState({
[e.target.name]: e.target.value,
});
};
render() {
const { title, body, status } = this.state;
return (
<div className="card card-body mt-4 mb-4">
<h2>New Post</h2>
<form onSubmit={this.onSubmit}>
<div className="form-group">
<label>Title</label>
<input
type="text"
name="title"
value={title}
onChange={this.onChange}
className="form-control"
/>
</div>
<div className="form-group">
<label>Content</label>
<textarea
type="text"
name="body"
rows="15"
value={body}
onChange={this.onChange}
className="form-control"
/>
</div>
<div className="form-group">
<button className="btn btn-primary" type="submit">
Submit
</button>
</div>
</form>
</div>
);
}
}
export default NewPost;
App.js:
import React from "react";
import NavBar from "./components/layout/navbar";
import store from "./store";
import { Provider } from "react-redux";
import Dashboard from "./components/posts/dashboard";
import NewPost from "./components/posts/form";
import {
HashRouter as Router,
Route,
Switch,
Redirect,
} from "react-router-dom";
class App extends React.Component {
render() {
return (
<Provider store={store}>
<Router>
<React.Fragment>
<div className="container">
<NavBar />
<Switch>
<Route exact path="/" component={Dashboard}></Route>
<Route exact path="/new" component={NewPost}></Route>
</Switch>
</div>
</React.Fragment>
</Router>
</Provider>
);
}
}
export default App;
Issue is not related to code. I created new react application and moved all my code now everything is working fine.
For some reason, my web app is not directing to the component whenever I go to the parameters. Specifically, it is not going to the Battle component.
Here is what the navigation looks:
import React from 'react';
import Header from './components/Header/Header';
import SelectPlayers from './pages/SelectPlayers/SelectPlayers';
import Popular from './pages/Popular/Popular'
import Battle from './pages/Battle/Battle'
import { BrowserRouter as Router, Route, Link, Switch } from "react-router-dom";
function App() {
return (
<Router>
<div className={'flex flex-column'}>
<Header />
<Switch>
<Route exact path={'/'} component={Popular}/>
<Route exact path={'/battle/select-player'} component={SelectPlayers} />
<Route exact path={'/results?playerOne=:playerOne&playerTwo=:playerTwo'} component={Battle} />
</Switch>
</div>
</Router>
);
}
export default App;
In the SelectPlayers component, whenever the user presses a button it runs:
import React, {useState} from 'react';
function SelectPlayers(props) {
const [playerOne, setPlayerOne] = useState('');
const [playerTwo, setPlayerTwo] = useState('');
function setPlayerName(event, player){
if (player === 1){
setPlayerOne(event.target.value)
} else if (player === 2) {
setPlayerTwo(event.target.value)
}
}
function goToBattle(event){
event.preventDefault();
props.history.push(`/results?playerOne=${playerOne}&playerTwo=${playerTwo}`)
}
return (
<div className={'pa3 mh7-l mh7-m'}>
<div className="flex flex-column">
<div className={'mb1'}>
<h1 className={'mb0'}>Player One</h1>
<input onChange={(e) => setPlayerName(e, 1)} type="text" placeholder={'github username'} className={'input-reset pa1 w-100 h2 ba b--black br2'}/>
</div>
<div className="tc dark-red">
<h1>Versus</h1>
</div>
<div className={'mb3'}>
<h1 className={'mb0 mt0 tr'}>Player Two</h1>
<input onChange={(e) => setPlayerName(e, 2)} type="text" placeholder={'github username'} className={'input-reset pa1 w-100 h2 ba b--black br2'}/>
</div>
<div>
<button onClick={(e) => goToBattle(e)} className={'input-reset pa1 h2 fw1 bg-black white ba w-100 b--black br2'}>Battle</button>
</div>
</div>
</div>
);
}
export default SelectPlayers;
On the Battle component, I write some console.log stuff just to check if the Component loaded. However, whenever I go to that parameter, none of the code in my componentDidMount is running. I don't see any of the console.logs I wrote in componentDidMount in my developer console. Here is the component:
import React, {Component} from 'react';
class Battle extends Component {
constructor(props){
super(props)
}
componentDidMount() {
console.log('runngins');
console.log(this.props);
}
render() {
return (
<div className={'pa3 mh7-l mh7-m'}>
<div className="flex flex-column">
</div>
</div>
);
}
}
export default Battle;
You can see the code at this repo: https://github.com/tarekgabarin/github_compete
It would be greatly appreciated if anyone helped me.
As per your new comment that code is working without queryset, looks like there is some problem with your queryset parameters.
As suggested in comment box, don't define Router with queryset.
<Switch>
<Route exact path={'/'} component={Popular}/>
<Route exact path={'/battle/select-player'} component={SelectPlayers} />
<Route exact path={'/results'} component={Battle} />
</Switch>
In your SelectPlayers component, navigate to next page with queryset.
props.history.push("/results?playerOne=" +playerOne+ "&playerTwo=" +playerTwo)
On Battle component, use (query-string) to read the parameter. For example:
const values = queryString.parse(this.props.location.search);
const player_one = values.playerOne
const player_two = values.playerTwo
Please note that my above code is not tested.
Thanks
I am trying to Redirect to the User Profile page once the user is logged in with valid username and password
But when I click the Login button, it doesn't go to the User Profile (but it changes the url as intended (http://localhost:3000/instructor/banuka)
It should also display InstructorLoginFormComponent which it doesn't. When I click nothing happens but all I can see is still there is the login page
This is my InstructorLoginForm.jsx
import React, { Component } from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import InstructorProfile from "./instructor-profile";
import InstructorLoginFormComponent from "./instructor-login-form-component";
export default class InstructorLoginForm extends Component {
constructor(props) {
super(props);
this.state = {
username: "",
password: ""
};
this.onChangeUsername = this.onChangeUsername.bind(this);
this.onChangePassword = this.onChangePassword.bind(this);
this.handleOnClick = this.handleOnClick.bind(this);
}
onChangeUsername(e) {
this.setState({
username: e.target.value
});
}
onChangePassword(e) {
this.setState({
password: e.target.value
});
}
handleOnClick(e) {
e.preventDefault();
const path = `/instructor/${this.state.username}`;
this.props.history.push(path);
}
render() {
return (
<Router>
<Switch>
<Route
exact
path="/login"
component={props => (
<InstructorLoginFormComponent
{...props}
username={this.state.username}
password={this.state.password}
handleOnClick={this.handleOnClick}
onChangeUsername={this.onChangeUsername}
onChangePassword={this.onChangePassword}
/>
)}
/>
<Route
path={"/instructor/:instructorId"}
component={InstructorProfile}
/>
</Switch>
</Router>
);
}
}
This is the InstructorLoginFormComponent.jsx
import React, { Component } from "react";
import { Link } from "react-router-dom";
export default class InstructorLoginFormComponent extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="container h-100" style={{ marginTop: 100 }}>
<div className="d-flex justify-content-center h-100">
<div className="user_card bg-dark">
<div className="d-flex justify-content-center">
</div>
<div
className="d-flex justify-content-center form_container"
style={{ marginTop: 0 }}
>
<form>
<div className="input-group mb-3">
<div className="input-group-append">
<span className="input-group-text bg-info">
<i className="fa fa-user" />
</span>
</div>
<input
value={this.props.username}
onChange={this.props.onChangeUsername}
type="text"
name="username"
className="form-control input_user"
placeholder="username"
/>
</div>
<div className="input-group mb-2">
<div className="input-group-append">
<span className="input-group-text bg-info">
<i className="fa fa-lock" />
</span>
</div>
<input
value={this.props.password}
onChange={this.props.onChangePassword}
type="password"
name="password"
className="form-control input_user"
placeholder="password"
/>
</div>
<div className="form-group">
<div className="custom-control custom-checkbox">
<input
type="checkbox"
className="custom-control-input"
id="customControlInline"
/>
<label
className="custom-control-label"
htmlFor="customControlInline"
style={{ color: "#ffffff" }}
>
Remember me
</label>
</div>
</div>
</form>
</div>
<div className="d-flex justify-content-center mt-3 login_container">
<Link
to={`/instructor/${this.props.username}`}
type="button"
className="btn login_btn bg-info"
>
Login
</Link>
</div>
</div>
</div>
</div>
);
}
}
And this is the InstructorProfile.jsx
import React, { Component } from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
export default class InstructorProfile extends Component {
render(){
return(
<div>
<h1>Welcome to the profile</h1>
</div>
);
}
}
Can someone please tell me where I have went wrong and suggest me a good way to redirect after login validation?
Resolved As Is
The problems you're experiencing are likely related to your router not being configured correctly. If you want your InstructorLoginFormComponent to be rendered on path /, you have to put it inside the route like below. You cannot just pass the reference to the component since you need to pass it the username, password, etc props. You should remove it from anywhere else in your render function since you only want it to render when the path is /. I also changed your instructor path to use :id. When you want to specify a url parameter, you need to use the syntax /<my_url>:<my_variable_name>. It's important to put the / out front of all your paths because it's a relative path if you don't.
New Router Component
<Router>
<Switch>
<Route exact path="/login" render={props => (
<InstructorLoginFormComponent
{...props}
username = { this.state.username }
password = { this.state.password }
handleOnClick = { this.handleOnClick }
onChangeUsername = { this.onChangeUsername }
onChangePassword = { this.onChangePassword }
/>
)} />
<Route path={"/instructor/:instructorId"} component={InstructorProfile} />
</Switch>
</Router>
Updated handleOnClick
handleOnClick(e) {
e.preventDefault();
const path = `/instructor/${this.state.username}`;
this.props.history.push(path);
}
Updated Login Button
<Link
to={`/instructor/${this.props.username}`}
type="button"
className="btn login_btn bg-info"
>
Login
</Link>
Recommendation Regarding Login
It's important to note that you aren't actually checking the username and password against something on the server. I'm not sure if this is your test code, or if you intend to implement something with REST. However, you should use a button instead of a link that calls a function on click that sends the username and password to the server. From there, it would return if the credentials matched and whether the user would be able to login or not. Then, you would use
this.props.history.push(`instructor/${this.state.username}`);
to forward the user to the instructor profile page. But you would only want to do that after you have checked their credentials. You would not use a Link component to perform those actions. I put an example below.
Example login button
<button
type="button"
className="btn login_btn bg-info"
onClick={this.props.handleOnClick}
>
Login
</button>
New handleOnClick
handleOnClick = async (e) => {
e.preventDefault();
//pseudo-code below
const user = await checkUserCredentialsAndGetUserObject(this.state.username, this.state.password);
if(user.response === 'success') {
this.setState({ user });
this.props.history.push(`/instructor/${this.state.username}`);
} else {
flashMessage('The user credentials you entered were incorrect.');
}
}
Ok, I'm not exactly sure what you're asking, but I think I know where you're going wrong.
First of all, when you use the exact keyword on a Route, it will literally only show when the url matches. So if you want the LoginFormComponent to show in addition to another, you need to remove the exact keyword.
Next, for dynamically rendering profile pages, you are not following how React Router works.
This tutorial gives a good overview: https://tylermcginnis.com/react-router-url-parameters/
The short of it is, for the Profile page, the path should be
<Route path='/instructor/:username' component={InstructorProfile} />
Then, in the profile component, set the initial state by accessing the username in the URL with
this.state = {
user: props.match.params.username
}
The question is a little vague, but I think this will help with what you're trying to do. Let me know if something is unclear.