how pass function between sibilngs components in reactjs - javascript

so i have 2 component are sibiling one call and
and i went to pass a function from NavBar component to Login component
i try to do that becouse i went when the user login i can call to function from the NavBar to update the state in the Navbar component
i add here the code of 2 components
this the NavBar component and i went to pass the userLog function from her
import axios from "axios";
import React, { useEffect, useState } from "react";
import { NavLink } from "react-router-dom";
import Cookies from 'js-cookie';
import Login from "../auth/login/Login";
import { useLocation } from 'react-router-dom';
import './navBar.css'
function NavBar() {
const [cookie, setCookie] = useState(null)
const userLog = () => {
if (localStorage.getItem("login")) {
setCookie(true)
}
}
const logout = async () => {
console.log("logout")
const res = await axios.get('http://127.0.0.1:3000/users/logout', { withCredentials: true })
.then(res => {
if (res.data.status == "success") {
console.log('sss')
setCookie(false)
localStorage.setItem('login', 'false')
}
})
}
return (
<div className="nav-bar-container">
<img className="imgLogo" src="data:image/png;base64,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" />
<ul className="main-nav">
<li><NavLink className="navLink" to="/">Home</NavLink></li>
<li><NavLink className="navLink" to="/">Contact</NavLink></li>
<li><NavLink className="navLink" to="/login">Login</NavLink></li>
{/* {cookie === true ? <li><NavLink className="navLink" to="/">username</NavLink></li> : <li><NavLink className="navLink" to="/login">Login</NavLink></li>} */}
<li><NavLink onClick={logout} className="navLink" to='/' >Logout</NavLink></li>
<li><NavLink className="navLink" to="/sign-in">Signin</NavLink></li>
{/* <button onClick={() => user()}>user</button> */}
{/* <button onClick={() => log()}>dddd</button> */}
</ul>
</div>
);
}
export default NavBar;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
this the Login component and i went to get the userLog function and call it in the login function that i call by onClick
import axios from "axios";
import React, { useState } from "react";
import { useNavigate } from "react-router-dom";
import { useLocation } from 'react-router-dom';
import './Login.css'
function Login(props) {
const [email, setEmail] = useState("")
const [password, setPass] = useState("")
const [cookie, setCookie] = useState(Boolean)
let navigate = useNavigate();
const location = useLocation()
const login = async (props) => {
console.log(email, password);
try {
const res = await axios.create({ withCredentials: true }).post(`http://127.0.0.1:3000/users/login`, {
email: email,
password: password
});
if (!res) {
return "not work"
}
console.log(res.data.data.user)
localStorage.setItem('login', 'true')
navigate("/", { replace: true });
} catch (error) {
console.log(error);
}
};
return (
<div className="pop-container">
<div className="popup">
<div className="content">
<h1>Sign in</h1>
<div className="input-field"><input placeholder="Email" className="validate" onChange={(e) => setEmail(e.target.value)} /></div>
<div className="input-field"><input placeholder="Password" className="validate" onChange={(e) => setPass(e.target.value)} /></div>
<button onClick={(e) => login(e)} className="second-button">Sign in</button>
<p>Don't have an account? Sign Up</p>
</div>
</div>
</div>
);
}
export default Login;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
I try to use useLocation and also pass state in NavLink in the NavLink component
but thay cant get a function
to pass some date like string i success its not a problem but function I not success

There isn't a direct way to pass between sibling components. One option is to pass from sibling 1 -> parent component -> sibling 2. So with your "NavBar" and "Login" components you can lift state up from Navbar to the App (parent) component and then down to the Login component.
Option 2 would be setting up something like a useContext hook. Context is for setting global state variables to avoid unnecessary prop drilling.

Related

useContext() returns null value

I am trying to use useContext to get the current logged in user details. I am getting an error in my console which says:
Uncaught TypeError: user is null
children Animate.jsx:79
Animate Animate.jsx:76
I am getting this error whenever I Logout. I am facing no errors in the console when I am logged in.
Here is the code:
Context.js File:
import { onAuthStateChanged } from "firebase/auth";
import { useEffect, useState } from "react";
import { createContext } from "react";
import { auth } from "./firebase";
export const Context = createContext()
const ContextProviderfunc=({children})=>{
const use={
"mail":'history'
}
const [user,setuser]=useState({});
useEffect(()=>{
const unsubscribe = onAuthStateChanged(auth,(currentUser)=>{
// console.log(currentUser);
setuser(currentUser);
})
return ()=>{
unsubscribe();
}
},[])
return(
<Context.Provider value={user}>
{children}
</Context.Provider>
)
}
export default ContextProviderfunc
Animate.jsx File:
import React from 'react'
import {
BrowserRouter as Router, Routes, Route, useLocation, Link
} from 'react-router-dom'
import Navbar from './Navbar'
import RecipeCard from './RecipeCard'
import Details from './Details'
import { AnimatePresence, motion } from "framer-motion"
import Add from './Add'
import clicked from './RecipeCard'
import { useState } from 'react'
import Login from './Login'
import Register from './Register'
import Protected from './Protected'
import Logout from './Logout'
import Myrecipe from './Myrecipe'
import { useContext } from 'react'
import { Context } from '../Context'
export default function Animate(props) {
const user=useContext(Context)
const [pname, setpname] = useState()
function assign(nigga) {
setpname(nigga);
console.log(nigga);
}
const location = useLocation()
return (
<motion.div className='all'
>
<AnimatePresence>
<Routes location={location} key={location.pathname}>
<Route path='/Login' element={<Login />} />
<Route path='/Register' element={<Register />} />
<Route path='*' element={[<Protected><Navbar /></Protected>, <h2>Recipes</h2>,
<div className='recipes'>
{props.query ? props.query.map((object, i) => (
<Link className='lin' to='/Details'>
<div onClick={() => assign(object.Name)}>
<Protected><RecipeCard
src={object.src}
name={object.Name}
ingredients={object.Ingredients}
steps={object.Steps}
key={i}
/></Protected>
</div></Link>
)) : "Loading"}
<Link className='lin' to='/Add'>
<div className='cardcont'>
<img src='https://cdn.pixabay.com/photo/2017/11/10/05/24/add-2935429_960_720.png' alt="1" className='cardimg' />
<div className='cardbody'>
<h3>Add</h3>
</div>
</div></Link>
</div>
]} />
<Route path='/MyRecipe' element={[<Protected><Navbar /></Protected>, <h2>My Recipes</h2>,
<Protected>
<div className='recipes'>
{props.query2 ? props.query2.map((object, i) => {
if(user.email==object.email){
console.log(user.email);
return(<Link className='lin' to='/Details'>
<div onClick={() => assign(object.Name)}>
<Protected><RecipeCard
src={object.src}
name={object.Name}
ingredients={object.Ingredients}
steps={object.Steps}
key={i}
/></Protected>
</div></Link>)
}
}
) : "Loading"}
<Link className='lin' to='/Add'>
<div className='cardcont'>
<img src='https://cdn.pixabay.com/photo/2017/11/10/05/24/add-2935429_960_720.png' alt="1" className='cardimg' />
<div className='cardbody'>
<h3>Add</h3>
</div>
</div></Link>
</div>
</Protected>
]} />
<Route path='/Details' element={
[<Navbar />, props.query ? props.query.map((object, m) => {
if (pname == object.Name) {
console.log(object.Name);
return (<Details
src={object.src}
name={object.Name}
Ingredients={object.Ingredients}
Steps={object.Steps}
key={m}
/>)
}
}) : "Loading"]} />
<Route path='/Add' element={[<Navbar />, <Add />]} />
</Routes>
</AnimatePresence>
</motion.div>
)
}
line 79 of Animate.jsx
if(user.email==object.email){
console.log(user.email);
App.js file:
import Navbar from './components/Navbar';
import RecipeCard from './components/RecipeCard';
import { useContext, useEffect, useState } from 'react';
import { auth, db, storage } from './firebase'
import { collection, addDoc, getDocs, doc } from "firebase/firestore";
import { async } from '#firebase/util';
import Details from './components/Details'
import Animate from './components/Animate';
import {BrowserRouter as Router, Route,Routes} from 'react-router-dom'
import Add from './components/Add';
import ContextProviderfunc from './Context';
import { Context } from './Context';
function App() {
const [query, setquery] = useState()
const [query2, setquery2] = useState()
const [recipe, setrecipe] = useState()
const add = async (e) => {
e.preventDefault()
try {
const docRef= await addDoc(collection(db, "recipe"),
{
src: 'https://images.immediate.co.uk/production/volatile/sites/30/2017/02/Two-panna-cotta-on-plates-298e616.jpg',
ingredients: ['Dish'],
steps: ['Description']
}, {
src: 'https://imagesvc.meredithcorp.io/v3/mm/image?url=https%3A%2F%2Fstatic.onecms.io%2Fwp-content%2Fuploads%2Fsites%2F43%2F2022%2F04%2F19%2F22749-the-best-banana-pudding-mfs-366-1x1-1.jpg',
ingredients: ['Dish'],
steps: ['Description']
});
// console.log('morein');
// console.log("Document written with ID: ", docRef.id);
} catch (error) {
console.log('morein');
console.error("Error adding document: ", error);
}
}
useEffect(() => {
const getrecipe = async () => {
const data = await getDocs(collection(db, 'recipe'))
const data2 = await getDocs(collection(db, 'userrecipe'))
console.log(data);
setquery(data.docs.map((doc) => (
{
...doc.data(), id: doc.id
})
))
setquery2(data2.docs.map((doc) => (
{
...doc.data(), id: doc.id
})
))
console.log(query);
};
getrecipe();
}, []);
useEffect(() => {
// searchRecipes();
}, [])
return (
<div>
<ContextProviderfunc>
<Router>
<Animate query={query} query2={query2}/>
</Router>
</ContextProviderfunc>
</div>
);
}
export default App;
These two files I think are needed to debug. If any more code is needed I will provide it. Please find out the issue in the above code
Whenever the user logs out, value of currentUser will become null in here:
onAuthStateChanged(auth,(currentUser)=>{
// console.log(currentUser);
setuser(currentUser); // currentUser is null
})
Since you are updating the state (setuser), The Context Provider rerenders and passes down null as value.
return(
<Context.Provider value={user}> // user is null, and is being passed down
{children}
</Context.Provider>
)
Within Animate you receive the null value.
const user=useContext(Context) // user is null, after loging out.
Long story short, user is at times null within Animate, so you don't have the guarantee to use properties like email, like this line below:
if(user.email==object.email){
console.log(user.email);
The solution: Wherever you need to use user, first check if it equals to null.
If you set logged out user ti be null, you should not expect to retrieve email address of null.
You should check (read from context) if user still logged in.
For example :
if(user) {
return <div>I am logged in user and my email is {user.email} </div>
}
return <div>You are not logged In </div>
Specific to your case: check first user and then user.email to avoid Uncaught TypeError: user is null
if(user && user.email === object.email) .....
This is just a friendly suggestion, which is not related with your question: Use ===instead of == for for information you can read this page

React Router - The Address Bar Is Changing But Not Rendering The Component

I Am Making A Search Functionality On My Website But Whenever I Type Something And Hit The Search Button The Address ( URL ) Changes But The Component Stays The Same And When I Refresh The Page After That The Component Appears On The Screen.
Here's A Picture Of What I Meant :
URL CHANGES BUT COMPONENT IS SAME
Here's A Picture After The Reload :
Should Be Like This When I Hit The Search
There Was A Solution I Found That Changing From Browser Router To Router Works But That Seems To Give The Following Error :
Uncaught TypeError: Cannot read properties of undefined (reading 'pathname')
App.js
import "./App.css";
import {
BrowserRouter as Router,
Route,
Switch,
Routes,
} from "react-router-dom";
import Header from "./Components/Layouts/Header";
import Footer from "./Components/Layouts/Footer";
import Home from "./Components/Home";
import ProductDetails from "./Components/Product/ProductDetails";
function App() {
return (
<Router>
<div className="App">
<Header />
<div className="container container-fluid">
<Routes>
<Route path="/" element={<Home />} exact />
<Route path="/search/:keyword" element={<Home />} />
<Route path="/product/:id" element={<ProductDetails />} />
</Routes>
</div>
<Footer />
</div>
</Router>
);
}
export default App;
History.js
import { createBrowserHistory } from "history";
const history = createBrowserHistory();
export default history;
Header Component
import React, { Fragment } from "react";
import { Route } from "react-router-dom";
import history from "./history";
import Search from "./Search";
const Header = () => {
return (
<Fragment>
<nav className="navbar row">
<div className="col-12 col-md-3">
<div className="navbar-brand">
<img src="./images/logo.png" />
</div>
</div>
<div className="col-12 col-md-6 mt-2 mt-md-0">
<Search history={history} />
</div>
<div className="col-12 col-md-3 mt-4 mt-md-0 text-center">
<button className="btn" id="login_btn">
Login
</button>
<span id="cart" className="ml-3">
Cart
</span>
<span className="ml-1" id="cart_count">
2
</span>
</div>
</nav>
</Fragment>
);
};
export default Header;
Search Component
import React, { useState } from "react";
const Search = ({ history }) => {
const [keyword, setKeyword] = useState("");
const searchHandler = (e) => {
e.preventDefault();
if (keyword.trim()) {
history.push(`/search/${keyword}`);
} else {
history.push("/");
}
};
return (
<form onSubmit={searchHandler}>
<div className="input-group">
<input
type="text"
id="search_field"
className="form-control"
placeholder="Enter Product Name ..."
onChange={(e) => setKeyword(e.target.value)}
/>
<div className="input-group-append">
<button id="search_btn" className="btn">
<i className="fa fa-search" aria-hidden="true"></i>
</button>
</div>
</div>
</form>
);
};
export default Search;
Home Component
import React, { Fragment, useState } from "react";
import { useParams } from "react-router-dom";
import Pagination from "react-js-pagination";
import MetaData from "./Layouts/MetaData";
import { useDispatch, useSelector } from "react-redux";
import { getAllProducts } from "../Actions/productActions";
import { useEffect } from "react";
import Product from "./Product/Product";
import Loader from "./Layouts/Loader";
import { useAlert } from "react-alert";
const Home = () => {
const [currentPage, setCurrentPage] = useState(1);
const dispatch = useDispatch();
const alert = useAlert();
const { loading, products, error, productsCount, resultPerPage } =
useSelector((state) => state.products);
const { keyword } = useParams();
useEffect(() => {
if (error) {
return alert.error(error);
}
dispatch(getAllProducts(keyword, currentPage));
}, [dispatch, error, alert, keyword, currentPage]);
function setCurrentPageNo(pageNumber) {
setCurrentPage(pageNumber);
}
return (
<Fragment>
{loading ? (
<Loader />
) : (
<Fragment>
<MetaData title="Buy At Discount Price" />
<h1 id="products_heading">Latest Products</h1>
<section id="products" className="container mt-5">
<div className="row">
{products &&
products.map((product) => (
<Product product={product} key={product._id} />
))}
</div>
</section>
{resultPerPage <= productsCount && (
<div className="d-flex justify-content-center mt-5">
<Pagination
activePage={currentPage}
itemsCountPerPage={resultPerPage}
totalItemsCount={productsCount}
onChange={setCurrentPageNo}
nextPageText={"Next"}
prevPageText={"Prev"}
firstPageText={"First"}
lastPageText={"Last"}
itemClass="page-item"
linkClass="page-link"
/>
</div>
)}
</Fragment>
)}
</Fragment>
);
};
export default Home;
Action
import axios from "axios";
import {
ALL_PRODUCTS_REQUEST,
ALL_PRODUCTS_SUCESS,
ALL_PRODUCTS_FAIL,
PRODUCT_DETAILS_REQUEST,
PRODUCT_DETAILS_SUCCESS,
PRODUCT_DETAILS_FAIL,
CLEAR_ERRORS,
} from "../Constants/productConstants";
// Get All Product ( ACTIONS )
export const getAllProducts =
(keyword = "", currentPage = 1) =>
async (dispatch) => {
try {
dispatch({ type: ALL_PRODUCTS_REQUEST });
const { data } = await axios.get(
`/api/v1/products?keyword=${keyword}&page=${currentPage}`
);
dispatch({
type: ALL_PRODUCTS_SUCESS,
payload: data,
});
} catch (error) {
dispatch({
type: ALL_PRODUCTS_FAIL,
payload: error.response.data.message,
});
}
};
// Product Details ( ACTIONS )
export const getProductDetails = (id) => async (dispatch) => {
try {
dispatch({ type: PRODUCT_DETAILS_REQUEST });
const { data } = await axios.get(`/api/v1/product/${id}`);
dispatch({
type: PRODUCT_DETAILS_SUCCESS,
payload: data.product,
});
} catch (error) {
dispatch({
type: PRODUCT_DETAILS_FAIL,
payload: error.response.data.message,
});
}
};
// Clearing eroor message
export const clearErrors = () => async (dispatch) => {
dispatch({ type: CLEAR_ERRORS });
};
You can try to use the useNavigate() hook instead of createBrowserHistory()
First import useNavigate
import { useNavigate } from 'react-router-dom';
then just do this after importing
const navigate = useNavigate();
const searchHandler = (e) => {
e.preventDefault();
if (keyword.trim()) {
navigate(`/search/${keyword}`);
} else {
navigate("/");
}
};

Navbar not updating after state change

As the title says my navbar is not changing the fragments after updating the state. I have no idea how to refresh it and other ideas seems to not work for me. I have tried to change the statements.
All i want to do is, after a user logs in successfully the state changes to true and the navbar updates with the corrent components. Thank you !
Home.js
import React, { useEffect } from 'react'
function Home() {
useEffect(()=>{
if(!localStorage.getItem("loggedIn")){
localStorage.setItem("loggedIn",false);
}
},[]);
return (
<div>
Home
</div>
)
}
export default Home
Login.js
import React from 'react';
import './Login.css';
import Axios from 'axios';
import { useEffect, useState } from "react";
import {useHistory} from 'react-router-dom';
function Login() {
const[username,setUsername] = useState('');
const[password,setPassword] = useState('');
const[errorMessage,setErrorMessage] = useState('');
let history = useHistory();
const login = () =>{
console.log(username);
Axios.post("http://localhost:3001/user/login",{username: username,password: password}).then((response) => {
//console.log(response);
if(response.data.loggedIn){
localStorage.setItem("loggedIn",true);
localStorage.setItem("username",response.data.username);
history.push('/');
}else{
setErrorMessage(response.data.message);
}
});
};
return (
<div className="Login">
<h1>Login to your BugTrack account !</h1>
<div className="LoginForm">
<input type="text" placeholder="USERNAME"
onChange={(event)=>{setUsername(event.target.value)}}/>
<input type="password" placeholder="PASSWORD"
onChange={(event)=>{setPassword(event.target.value)}}/>
<button onClick={login}>Login to you account</button>
<h1 style={{color: "red"}}>{errorMessage}</h1>
</div>
</div>
);
}
export default Login
Navbar.js
import React, { useEffect, useState, Component, Fragment } from 'react';
import './Navbar.css';
function Navbar() {
const [loggedIn, setLoggedIn] = useState(false);
useEffect(()=> {
setLoggedIn(localStorage.getItem("loggedIn"));
},[localStorage.getItem("loggedIn")]);
return (
<div className="Navbar">
Home
{!loggedIn ? (
<Fragment>
Profile
</Fragment>
):(
<Fragment>
Register
Login
</Fragment>
)}
</div>
);
}
export default Navbar;
You want to to use localStorage as a useEffect dependency which isn't supports for React to rerender/update the component. Check this: useEffect do not listen for localStorage - it's like duplicate of your question.

onAuthStateChanged Firebase Listener on app refresh causing private route issues

I have currently initialized a React App with firebase. Within the application, I have created an open login route and a private Home route using react-router-dom. my app.js looks like so:
App.js:
import React from 'react'
import { BrowserRouter, Switch, Route } from 'react-router-dom'
import Login from './pages/Login'
import Home from './pages/Home'
import PrivateRoute from './utils/PrivateRoute'
const App = () => {
return (
<BrowserRouter>
<Switch>
<PrivateRoute exact path='/' component={Home} />
<Route path='/login' component={Login} />
</Switch>
</BrowserRouter>
)
}
export default App
I am storing the currentUser in context using the onAuthStateChanged firebase event listener like so:
AppContext:
import { useEffect, useState, createContext } from 'react'
import { auth } from '../utils/firebase'
export const AppContext = createContext()
export const AppProvider = ({ children }) => {
const [currentUser, setCurrentUser] = useState(null)
useEffect(() => {
auth.onAuthStateChanged(setCurrentUser)
}, [])
return (
<AppContext.Provider value={{ currentUser }}>
{children}
</AppContext.Provider>
)
}
when a user logins in via the login route:
login:
import React, { useState, useCallback, useContext } from 'react'
import { auth } from '../utils/firebase'
import { useHistory, Redirect } from 'react-router-dom'
function Login() {
const [formData, setFormData] = useState({ email: '', password: '' })
const history = useHistory()
const handleChange = ({ target: { name, value } }) => {
setFormData({ ...formData, [name]: value })
}
const handleSubmit = useCallback(
async event => {
event.preventDefault()
await auth
.createUserWithEmailAndPassword(formData.email, formData.password)
.then(user => {
console.log(user)
history.push('/')
})
.catch(err => {
alert(err)
})
},
[history, formData.email, formData.password]
)
return (
<div className='form-container sign-up-container'>
<form className='register-form' onSubmit={handleSubmit}>
<h1>Create Account</h1>
<div className='social-container'>
<div className='social'>
<i className='fab fa-facebook-f'></i>
</div>
<div className='social'>
<i className='fab fa-google-plus-g'></i>
</div>
<div className='social'>
<i className='fab fa-linkedin-in'></i>
</div>
</div>
<span>or use your email for registration</span>
<input
type='email'
placeholder='Email'
name='email'
onChange={handleChange}
/>
<input
type='password'
placeholder='Password'
name='password'
onChange={handleChange}
/>
<button type='submit'>Sign Up</button>
</form>
</div>
)
}
export default Login
the currentUser is successfully stored in context and the user is pushed into the private Home route.
the Private Route looks like so:
import React, { useContext } from 'react'
import { Route, Redirect } from 'react-router-dom'
import { AppContext } from '../context/AppContext'
const PrivateRoute = ({ component: Component, ...rest }) => {
const { currentUser } = useContext(AppContext)
return (
<Route
{...rest}
render={routeProps =>
!!currentUser ? (
<Component {...routeProps} />
) : (
<Redirect to={'/login'} />
)
}
/>
)
}
export default PrivateRoute
The issue I'm having is that when the app refreshes, the currentUser becomes null initially and then currentUser's information loads back up. While the currentUser is null on refresh, the user is kicked from the home route and redirected to the login page. I'm wondering if anyone has any suggestions on how to prevent this from happening.
You initialize your currentUser state variable with a null value:
const [currentUser, setCurrentUser] = useState(null)
This means that currentUser is always initially null when the page is first loaded. The prior user object isn't known for sure until some time later, after it's asynchronously loaded by the Firebase SDK. Your code needs to be ready for this. If you require that a user be signed in before rendering your component, you should wait for the first time onAuthStateChanged triggers with an actual user object.
You can read more about this behavior of the Firebase Auth SDK in this blog.

How do I link routes in React with react-router-dom dynamically using button?

I am very new to using React and JSX and was unsure how to dynamically set a link in my react app. I am trying to make it so that on the login page, if the user successfully inputs their username and password, it redirects to the homepage, and if they do not match it would reload the login page. This login form makes a fetch call to a Flask API, which returns success on a correct matching of the username and password with my database. The button that links the login page to the signup page currently works, but using similar logic does not work with the login button
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { BrowserRouter } from 'react-router-dom';
import * as serviceWorker from './serviceWorker';
import 'bootstrap/dist/css/bootstrap.css';
import 'semantic-ui-css/semantic.min.css'
ReactDOM.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>,
document.getElementById('root')
);
App.js
import React, {useEffect, useState, Component} from 'react';
import './App.css';
import {NewUserInfo} from './components/NewUserInfo';
import {Login} from './components/Login';
import {Register} from './components/Register';
import {Container} from "semantic-ui-react";
import {BrowserRouter, Route, Switch} from 'react-router-dom';
import Homepage from './components/Homepage';
import Tabs from './components/Tabs';
//import Header from './components/Header';
import Journal from './components/Journal';
import Survey from './components/Survey';
import Profile from './components/Profile';
function App(){
return (
<div>
<Switch>
<Route exact path="/" component={Login}/>
<Route path="/register" component={Register}/>
<Route path="/homepage" component={Homepage}/>
<Route path="/profile" component={Profile}/>
</Switch>
</div>
);
}
export default App;
Login.js
import React, { Component, useState } from "react";
import {Button, Form, FormGroup, Label, Input} from 'reactstrap';
export const Login = () => {
const [email_or_username, setName] = useState('');
const [password, setPassword] = useState('');
const [is_contributor, setContributor] = useState(false);
var ref = "/";
return (
<div>
<Form className="login-form">
<h1>
<div className="text-right">
<Button
href="/register"
className=" btn-dark text-right">
sign up
</Button>
</div>
<span className="font-weight-bold">Mindify</span>
</h1>
<FormGroup>
<Label>Username or Email</Label>
<h2></h2>
<Input
value={email_or_username}
placeholder = "Username or Email"
onChange={e => setName(e.target.value)}/>
</FormGroup>
<FormGroup>
<label>Password</label>
<h2></h2>
<Input
value={password}
placeholder = "Password"
onChange={e => setPassword(e.target.value)}/>
</FormGroup>
<FormGroup>
<div className="text-center">
<Input
type="checkbox"
value={is_contributor}
onChange={e => setContributor(e.target.checked)}/>
Contributor
</div>
</FormGroup>
<Button onClick={async () =>{
const login = {email_or_username, password, is_contributor};
console.log(JSON.stringify(login));
const response = await fetch('http://127.0.0.1:5000/api/login', {
method: 'POST',
headers:{
'Content-Type': 'application/json'
},
body: JSON.stringify(login)
})
.then(response => {
console.log(response.status);
if (response.status === 201) {
console.log("Successful Login");
ref="/homepage";
console.log(ref);
//redirect to home page
}
else if (response.status === 204) {
console.log("Invalid Username or Password or Incorrect Permissions");
ref="/";
console.log(ref);
// reload login page
}
})
.catch(error => console.log(error))
}}
href={ref}
className="btn-lg btn-dark btn-block">
Log in</Button>
</Form>
</div>);
}
In this case, you can use history via useHistory to dynamically change the route.
You get the history object in the component with: const history = useHistory()
Then you can dynamically push or replace the current route using: history.push(ref) or history.replace(ref)
import React, { Component, useState } from "react";
import {Button, Form, FormGroup, Label, Input} from 'reactstrap';
import {useHistory} from 'react-router-dom'
export const Login = () => {
const [email_or_username, setName] = useState('');
const [password, setPassword] = useState('');
const [is_contributor, setContributor] = useState(false);
const history = useHistory()
return (
<div>
<Form className="login-form">
<h1>
<div className="text-right">
<Button
href="/register"
className=" btn-dark text-right">
sign up
</Button>
</div>
<span className="font-weight-bold">Mindify</span>
</h1>
<FormGroup>
<Label>Username or Email</Label>
<h2></h2>
<Input
value={email_or_username}
placeholder = "Username or Email"
onChange={e => setName(e.target.value)}/>
</FormGroup>
<FormGroup>
<label>Password</label>
<h2></h2>
<Input
value={password}
placeholder = "Password"
onChange={e => setPassword(e.target.value)}/>
</FormGroup>
<FormGroup>
<div className="text-center">
<Input
type="checkbox"
value={is_contributor}
onChange={e => setContributor(e.target.checked)}/>
Contributor
</div>
</FormGroup>
<Button onClick={async () =>{
const login = {email_or_username, password, is_contributor};
console.log(JSON.stringify(login));
const response = await fetch('http://127.0.0.1:5000/api/login', {
method: 'POST',
headers:{
'Content-Type': 'application/json'
},
body: JSON.stringify(login)
})
.then(response => {
console.log(response.status);
if (response.status === 201) {
console.log("Successful Login");
const ref="/homepage";
console.log(ref);
//redirect to home page
history.push(ref)
}
else if (response.status === 204) {
console.log("Invalid Username or Password or Incorrect Permissions");
const ref="/";
console.log(ref);
// reload login page
history.push(ref)
}
})
.catch(error => console.log(error))
}}
className="btn-lg btn-dark btn-block">
Log in</Button>
</Form>
</div>);
}
In v6 of react-router-dom you would use useNavigate()
https://reactnavigation.org/docs/use-navigation/
You can either use "Navigate" as a return (It worked for me only in the beginning when a component mounts (no function call)):
import { Navigate } from 'react-router-dom';
function X() {
if (roles && currentUser && roles.indexOf(currentUser.role) === -1) {
return (<Navigate to='/notfound'></Navigate>);
}
...
} export { X };
Or you can use "useNavigate". For "useNavigate" you have to create a const first and in a function call you do it with return too -> return navigate("/..."):
import { useNavigate } from 'react-router-dom';
function LoginPage() {
const navigate = useNavigate();
const handleLogin = () => {
if (!(user.username && user.password)) {
return;
}
UserService.login(user)
.then(data => {
console.log("login successful")
return navigate("/");
}, error => {
setErrorMessage("Username or password is wrong.");
console.log("log in failed.")
});
}
...
} export { LoginPage };
So Navigate for while component is getting mounted (no function call) and useNavigate for when function calls.

Categories

Resources