React-Router - Pass Method to Child Component - javascript

I have a button nested within a component called "Create" that has to trigger a change in state that changes the state in app.js and renders a fresh view.
I can't seem to pass the method changeHPage from app.js to the Create component. I am using React-Router and normally I would simply write <App changeHPage={this.changePage}> to pass the method to its child component and call it using this.props.changeHpage but I can't pass props via this method when using React Router.
Any help on how to pass a method to a child component using React Router would be much appreciated. My code can be found below.
app.js:
/* STRICT MODE: See `../../server.js` */
'use strict';
/* GLOBAL REACT REQUIRES */
// React.js
const React = require('react');
// React-DOM for HTML rendering
const ReactDOM = require('react-dom');
// React router for dynamic pathing. Has several component features that need to be required to use.
const ReactRouter = require('react-router');
// 4 components pulled from ReactRouter:
const Router = ReactRouter.Router;
const Route = ReactRouter.Route;
const Navigation = ReactRouter.Navigation;
const Link = ReactRouter.Link;
const browserHistory = ReactRouter.browserHistory;
/* Relative paths to external components */
const auth = require('./helpers/auth.js');
const requireAuth = require('./helpers/requireauth.js');
const About = require('./components/about.js');
const Login = require('./components/login.js');
const Logout = require('./components/logout.js');
const Signup = require('./components/signup.js');
const Header = require('./components/header.js');
const Create = require('./components/create.js');
const NotFound = require('./components/notfound.js');
const Veri = require('./components/veri.js');
/* React App Creation */
const App = React.createClass({
// Declares the initial state when app is loaded
getInitialState : function() {
return {
loggedIn: auth.loggedIn(),
change: true,
phoneNumber: {}
}
},
// Updates state when login is trigger
updateAuth : function(loggedIn) {
this.setState({
loggedIn: loggedIn
})
},
changeHPage: function() {
this.state.change = !this.state.change;
this.setState({
change: !this.state.change
});
console.log("changePage On HomePage Pressed");
this.context.router.push('/')
},
// Login even triggered and sent to back-end
componentWillMount : function() {
auth.onChange = this.updateAuth
auth.login()
},
addNumber: function(phonenumber){
this.state.phonenumber = phonenumber
this.setState()
},
// Renders App and all of its children
render : function() {
<div className="Detail">
{this.props.children && React.cloneElement(this.props.children, {
changeHPage: this.changeHPage
})}
</div>
var firstView;
{if(this.state.change) {
firstView = <div>
<div className="row">
<Veri> This is a child of Veri </Veri>
<Header details="Hi, I'm Plantee"/>
<section className="col s12">
<ul>
{this.state.loggedIn ? (
<div>
<li><Link to="/logout">Log out</Link> </li>
<li><Link to="/create">Create Your Plantee</Link></li>
{/*<Create> <Veri/> </Create>*/}
</div>
) : (
<div>
<li><Link to="/login">Log In</Link></li>
<li><Link to="/signup">Sign up</Link></li>
</div>
)}
<li><Link to="/about">About</Link></li>
</ul>
{this.props.children || <p>You are {!this.state.loggedIn && 'not'} logged in.</p>}
</section>
</div> </div>
} else {
firstView= <div>'Hello'</div>
}
return React.cloneElement(
firstView,
{switch: this.changeHPage}
)
}}
})
/* React router initialization */
var routes = (
<Router history={browserHistory}>
<Route path="/" component={App} >
<Route path="header" component={Header} />
<Route path="login" component={Login} />
<Route path="logout" component={Logout} />
<Route path="create" component={Create} change={App.changeHPage} />
<Route path="signup" component={Signup} />
<Route path="about" component={About} />
<Route path="very" component={Veri} />
</Route>
<Route path="*" component={NotFound} />
</Router>
)
ReactDOM.render(routes, document.querySelector('#container'))
create.js:
const React = require('react');
const ReactDOM = require('react-dom');
const auth = require('../helpers/auth')
const Veri = require('./veri.js');
const App = require('../app.js');
const ReactRouter = require('react-router');
// 4 components pulled from ReactRouter:
const Router = ReactRouter.Router;
const Route = ReactRouter.Route;
const Navigation = ReactRouter.Navigation;
const Link = ReactRouter.Link;
const browserHistory = ReactRouter.browserHistory;
const Create = React.createClass({
getInitialState: function(){
return {checked: false}
},
handleClick: function(event) {
event.preventDefault();
this.setState({checked: !this.state.checked})
let phonenumber = {
phonenumber: this.refs.phonenumber.value
}
},
showVerification : function(event) {
event.preventDefault();
},
remove(e) {
e.preventDefault();
console.log(this.props);
},
render : function(){
var msg;
{if(this.state.checked) {
msg = <div><Veri text={'Your verification code is '} code={'code'}/> <form className="gotIt" onSubmit={this.props.changeHpage} >
<input type="Submit" value="Got It" />
</form> </div>
}
else {
msg = <Veri details={''}/>
}}
return (
<div>
<h1>Create Your Plantee</h1>
<h2>Please Enter Your Phone Number</h2>
<p>You will recieve a phone call in order to verify that you are capable of raising a plantee</p>
<form className="telephoneNumber" onSubmit={this.handleClick}>
<input id="phonenumber" ref="phonenumber" type="tel" />
<input type="Submit" />
</form>
<div> {msg} </div>
<h3>{this.props.children}</h3>
</div>
)
}
})
module.exports = Create;

Please see the following github issue:
https://github.com/reactjs/react-router/issues/1857
this is directly taken from: ryanflorence
Usually if you're passing props across route boundaries your parent route knows exactly what it's rendering:
<Route path="/inbox" component={Inbox}>
<Route path=":messageId" component={Message}/>
<IndexRoute component={InboxStats}/>
</Route>
const Inbox = React.createClass({
render() {
return (
<div>
{/* this is only ever `Message`, except the weird case
of `InboxStats` which doesn't need the prop */}
{React.cloneElement(this.props.children, {
onDelete: this.handleMessageDelete
})}
</div>
)
}
})
Instead, use a componentless route and just do "normal" React stuff.
<Route path="/inbox" component={Inbox}>
{/* no more `Message` */}
<Route path=":messageId"/>
</Route>
const Inbox = React.createClass({
render() {
const { messageId } = this.props.params
return (
<div>
{messageId ? (
<Message onDelete={this.handleMessageDelete}/>
) : (
<InboxStats/>
)}
</div>
)
}
})
cloneElement is not bad practice on its own, but it can often be an indicator that there's a bit more straightforward way of doing something.

Related

Input loses focus when I try to change an array element within an object managed by state hooks

The following is probably the neatest code that illustrates my problem I could think of. I am essentially trying to draw a table to the page that pulls exercise data from a database, sets that data to a state of an array of objects, and then when a row is clicked, render an 'edit' row below it that allows the user to change any of the array data inside one of those objects.
import React, {useEffect} from "react";
import { useState } from "react";
import { useNavigate } from "react-router";
export default function GetProgram() {
// holds all n number of exercises from database
const [exercises, setExercises] = useState([])
// number that holds which exercises is currently being shown/edited
const [editingExerciseIndex, setEditingExerciseIndex] = useState(-1)
// once both effects have fetched the data
const [loading, setLoading] = useState([true, true])
// holds info about the program to map for exercise displaying
const [program, setProgram] = useState({
days: [],
name : '',
_id : null
})
useEffect(() => {
async function getProgramInfo() {
fetch(`http://localhost:5000/program`).then((res) => {
res.json().then((body) => {
setProgram(body)
setLoading([false, loading[1]])
})
})
.catch((err) => {
console.log(`**ERR: ${err}`)
return
})
}
getProgramInfo()
}, [])
useEffect(() => {
async function getExercises() {
fetch(`http://localhost:5000/program/getmap`).then((res) =>{
res.json().then((body) => {
setExercises(body)
setLoading([loading[0], false])
})
})
}
getExercises()
}, [])
// onChange handler for edit fields
function updateField(props) {
var newExercise;
if (props.reps) {
const newReps = exercises[props.j].reps.map((r, i) => {
if (i === props.set-1) {
return parseInt(props.reps,10) // user's input
}
return r // old value
})
newExercise = {...exercises[props.j], reps:newReps} // []
console.log(newExercise)
}
else {
const newWeight = exercises[props.j].weight.map((w, i) => {
if (i === props.set-1) {
return parseInt(props.weight,10)
}
return w
})
newExercise = {...exercises[props.j], weight:newWeight}
}
console.log([...exercises, newExercise])
setExercises(exercises.map((exercise, i) => {
if (exercise.day === newExercise.day && exercise.position === newExercise.position) {
return newExercise
}
return exercise
}))
}
const EditFieldRow = (props) => {
console.log("Rendering Edit Field Row")
return (
<tr>
<td>{props.set}</td>
<td><input key="reps-input" type="text" value={props.reps} onChange={(e) => updateField({j:props.j, set:props.set, reps:e.target.value})}/></td>
<td><input key="weight-input" type="text" value={props.weight} onChange={(e) => updateField({j:props.j, set:props.set, weight:e.target.value})}/></td>
</tr>
)
}
const EditField = (props) => {
return (
<div>
<form onSubmit={() => console.log("submitted")}>
<table className="table table-bordered table-colored">
<thead>
<tr>
<th>Set</th>
<th>Reps</th>
<th>Weight</th>
</tr>
</thead>
<tbody>
{[...Array(props.exercise.sets).keys()].map((i) => {
return (
<EditFieldRow j={props.j} key={`fieldrow-${i}`} set={i+1} reps={props.exercise.reps[i]} weight={props.exercise.weight[i]}/>
)
})}
</tbody>
</table>
</form>
</div>
)
}
const PageContent = (props) => {
return (
<div className="container-fluid page-content program-page" >
<h2 style={{textAlign:'center'}}>{program.rname??program.name} Program</h2>
<div className="row">
{program.dayMap.map((day, i) => {
return (
<div className="col" key={`${day}-${i}`}>
<h4>{day}</h4>
<hr />
<table className="lift-table table table-bordered table-colored">
<thead>
<tr>
<th>Name</th>
<th>Sets</th>
</tr>
</thead>
{exercises.map((exercise, j) => {
if (exercise.day === i+1) {
return (
<tbody key={`${exercise.name}${i}${j}${day}`}>
<tr id={`exercise-row-${exercise.name.replaceAll(" ", "-")}`} className={`exercise-row`}
onClick={() => {
setEditingExerciseIndex(j)
}}
key={`${exercise.name}-${i}-${day}`}
>
<td>{exercise.name}</td>
<td>{exercise.sets}</td>
</tr>
{editingExerciseIndex === j && <tr><td colSpan="2">
<EditField exercise={exercises[j]} j={j}/>
</td></tr>}
</tbody>
)
}
})}
</table>
</div>
)
})}
</div>
</div>
)
}
if (program.dayMap) {
return (
<PageContent />
)
}
return (
<div></div>
)
}
The exercises array would look something like this
{
"program" : "Full-body-3d",
"name" : "Bench Press",
"position" : 1,
"day" : 1,
"sets" : 3,
"reps" : [
6, 6, 6
],
"ref" : "Bench",
"weight" : [
80, 80, 80
]
},
{
"program" : "Full-body-3d",
"name" : "Lat Pulldown",
"position" : 2,
"day" : 1,
"sets" : 3,
"reps" : [
12, 12, 12
],
"ref" : "Accessory",
"weight" : [
80, 80, 80
]
},
...
Where
position - order of the exercise to perform
day - day that is mapped to said exercise (ex. 1 might represent "Push day")
sets - will always be the length of reps[] and weight[] (not strictly necessary)
the rest aren't extremely important
This is my App.js that routes all pages:
import React, { useEffect, useState } from "react";
// We use Route in order to define the different routes of our application
import { Route, Routes } from "react-router-dom";
import { useCookies } from 'react-cookie';
// We import all the components we need in our app
import NB from "./components/navbar";
import WorkoutCalendar from "./components/workoutCalendar";
import Edit from "./components/edit";
import Create from "./components/create";
import Settings from "./components/settings";
import Header from "./components/header";
import Progress from "./components/progress";
import DayInfo from "./components/dayInfoPage";
import Delete from "./components/deleteall";
import Todo from "./components/dev/todo";
import GetProgram from "./components/lift/programPage";
import AddLift from "./components/lift/create";
import Populate from "./components/dev/pop_db";
import PopulateColorThemes from "./components/dev/pop_colors";
import UserList from "./components/user/list";
import UserLogin from "./components/user/login";
import Rec from "./components/rec";
const App = () => {
const [cookies, setCookie] = useCookies(['theme'])
const [loading, setLoading] = useState(true)
useEffect(() => {
setCookie('ColorTheme', cookies.ColorTheme ?? 'Ender', {path:'/'})
setLoading(false)
},[])
const [username, setUsername] = useState('');
if (loading) {
return (
<div className="page" style={{backgroundColor:'grey'}}></div>
)
}
else {
return (
<div className="page" data-theme={cookies.ColorTheme??'Ender'}>
{/* navbar */}
<NB />
{/* header */}
<Header username={username}/>
{/* content */}
<div className="page-content-area">
<Routes>
<Route exact path="/" element={<WorkoutCalendar />} />
<Route path="/edit/:id" element={<Edit />} />
<Route path="/record/create" element={<Create />} />
<Route path="/settings" element={<Settings />} />
<Route path="/record/:date" element={<DayInfo />} />
<Route path="/deleteall" element={<Delete />} />
<Route path="/dev/todo" element={<Todo />} />
<Route path="/program" element={<GetProgram />} />
<Route path="/lift/add" element={<AddLift />} />
<Route path="/program/populate" element={<Populate />} />
<Route path="/user" element={<UserList />} />
<Route path="/user/login" element={<UserLogin headerUsername={setUsername}/>} />
<Route path="/color/populate" element={<PopulateColorThemes />} />
<Route path="/record" element={<Rec />} />
</Routes>
</div>
</div>
);
}
};
export default App;
And finally index.js which renders the components
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { BrowserRouter } from "react-router-dom";
import {CookiesProvider} from 'react-cookie'
import 'bootstrap/dist/css/bootstrap.min.css';
import 'bootstrap';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<CookiesProvider>
<BrowserRouter>
<App />
</BrowserRouter>
</CookiesProvider>
</React.StrictMode>,
);
I cannot seem to find where I am going wrong despite reading tons of other posts with similar situations, but different causal issues. I've looked at In React ES6, why does the input field lose focus after typing a character?
and it seems like its almost always caused by rendering a form in a function inside render(). I don't quite know where that would be happening or how to avoid it.
I tried refactoring all of the components into the smallest pieces possible, keeping them all clumped as one. I tried adding keys to every element that would be effected, using an individual state hook that would track only the selected exercise and edit/display based off that rather than directly editing the single exercises state array. I expected this to be a solved problem, since I cannot imagine I am the first person to want to do something like this with a data structure similar to mine

Function is undefined in react

I have a function named Addphoto and I am passing that by props but I am getting an error that Addphoto is not a function
this is my main.js where i created routes
<div>
<Routes>
<Route exact path="/" element={<><Title todo={'PhotoFrame'} /> <PhotoFrame PhotoBlock={this.state.allPhotos} onRemovePhoto={this.removePhoto} /></>}/>
<Route path="/AddPhoto" element={<AddPhoto onAddPhoto={(addedPhoto)=>{
console.log(addedPhoto)
}}/>} />
</Routes>
</div>
here is the code of addphoto.js where i creatd ny function
class AddPhoto extends Component{
constructor(){
super()
this.handlesSubmit = this.handlesSubmit.bind(this)
}
handlesSubmit(event){
event.preventDefault();
const imageLink = event.target.elements.link.value
const description = event.target.elements.description.value
const photo = {
id: 0,
description:description,
imageLink:imageLink
}
console.log(photo)
if(description&&imageLink){
// this.props.onAddphoto(photo)
this.props.AddPhoto()
}
}
render(){
return(
<div>
<h1>photo frame</h1>
<div className='form'>
<form onSubmit={this.handlesSubmit}>
<input type="text" placeholder='Link' name='link'/>
<input type="text" placeholder='Description' name='description'/>
<button>Submit</button>
</form>
</div>
</div>
)
}
}
here is the error is occuring in my console
So, you gave a different name to the prop -onAddPhoto.
If you want to have access to addPhoto function inside AddPhoto.js:
const addPhoto = () => {
// functionality to add a photo
}
<Route path="/AddPhoto" element={<AddPhoto addPhoto={addPhoto}/>
Now you can call this.props.addPhoto() inside AddPhoto component.

DOM render not triggering after updating hooks

I am trying to delete an item (const removeItem) from a list using an onClick event in React.
The state is managed with hooks. I know my way of deleting the item is not the right way yet (i'm putting the name to null), but this is not the issue.
After i set a user to null (i update the users object), i expect a render to happen (useEffect) but it does not. If i switch components and go back to this one, it works, but when clicking the X button, nothing happens in the view.
component Home:
import React, { Suspense, useState, useEffect } from "react";
const Home = props => {
const [users, setUsers] = useState(props.users);
console.log(users);
useEffect(() => {}, [users]);
const addItem = e => {
users.push(e);
console.log(e);
e.preventDefault();
};
const removeItem = item => {
users.forEach(user => {
if (user.id === item) {
user.name = null;
}
});
console.log(users);
setUsers(users);
};
return (
<div className="Home">
<form className="form" id="addUserForm">
<input
type="text"
className="input"
id="addUser"
placeholder="Add user"
/>
<button className="button" onClick={addItem}>
Add Item
</button>
</form>
<ul>
{users.map(item => {
return (
<>
<li key={item.id}>{item.name + " " + item.count}</li>
<button className="button" onClick={() => removeItem(item.id)}>
X
</button>
</>
);
})}
</ul>
</div>
);
};
export default Home;
How i get my users object:
import React from "react";
const LotsOfUsers =[...Array(100).keys()].map((item, key) => item = {
name : `User ${key}`,
id: key,
count: 0
})
export default LotsOfUsers;
main App:
import React from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link,
useRouteMatch,
useParams
} from "react-router-dom";
import Home from "./Home"
import CTX from './store'
import LotsOfUsers from "./LotsOfUsers";
export default function App() {
return (
<CTX.Provider value={{}}>
<Router>
<div>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/topics">Topics</Link>
</li>
</ul>
<Switch>
<Route path="/about">
<About />
</Route>
<Route path="/topics">
<Topics />
</Route>
<Route path="/">
<Home users={LotsOfUsers} text="hello world"/>
</Route>
</Switch>
</div>
</Router>
</CTX.Provider>
);
}
function About() {
return <h2>About</h2>;
}
function Topics() {
let match = useRouteMatch();
return (
<div>
<h2>Topics</h2>
<ul>
<li>
<Link to={`${match.url}/components`}>Components</Link>
</li>
<li>
<Link to={`${match.url}/props-v-state`}>
Props v. State
</Link>
</li>
</ul>
{/* The Topics page has its own <Switch> with more routes
that build on the /topics URL path. You can think of the
2nd <Route> here as an "index" page for all topics, or
the page that is shown when no topic is selected */}
<Switch>
<Route path={`${match.path}/:topicId`}>
<Topic />
</Route>
<Route path={match.path}>
<h3>Please select a topic.</h3>
</Route>
</Switch>
</div>
);
}
function Topic() {
let { topicId } = useParams();
return <h3>Requested topic ID: {topicId}</h3>;
}
Looking into your code, I've noticed 2 times that you change users without the use of setUsers.
const addItem = e => {
users.push(e); // <--- here
console.log(e);
e.preventDefault();
};
const removeItem = item => {
users.forEach(user => {
if (user.id === item) {
user.name = null; // <--- here
}
});
console.log(users);
setUsers(users);
};
In that way, you are updating your users, without letting react know about it. On both cases you have to update users with setUsers and not mutating the array directly.
const addItem = e => {
setUsers(users.concat(e)); // sidenote: if e is your event, then you might be looking for e.target.value here
console.log(e);
e.preventDefault();
};
const removeItem = item => {
setUsers(users.filter(user => user.id !== item));
};
Both .concat() and .filter() use your users array, and return a new array based on the changes you want to apply, and then is used by setUsers to update your users.
So, in extend what I'm actualy doing on removeItem is:
const removeItem = item => {
const newUsers = users.filter(user => user.id !== item); // users remains untouched
setUsers(newUsers);
};
Also, you don't need useEffect hook for this scenario to work properly.
I hope this solves your problem.
You are committing THE Fundamental sin of the react universe. "Mutation"
of state.
const removeItem = item => {
users.forEach(user => {
if (user.id === item) {
user.name = null; // <--- HERE
}
});
console.log(users);
setUsers(users);
};
Check this codesandbox for a working demo of this issue.
https://codesandbox.io/s/jovial-panini-unql4
The react reconciler checks to see whether the 2 objects are equal and since you have just mutated and set the value it registers as the same object and there will be no state change triggered. Hence the view will not be re-rendered and useEffect will not be triggered.

how to Stop rerendering of entire component onChange event on input text field in reactJs

I m new to reactJs and i m creating user Authentication functionality. I have two components one is header which has navbar and it contains react-router routers and the other is login component which has two input fields ... The problem with login component is when i start typing in input field it loses focus after each character typed i know it is rerendering the whole component but i don't know how to solve this problem
header.js
changeName = (e) => {
this.setState({name : e.target.value})
}
changePass = (e) => {
this.setState({password:e.target.value})
}
login = () => {
var name = this.state.name;
var password = this.state.password
var mysession;
$.ajax({
url : 'http://localhost:4000/login',
type : "POST",
data : {username:name,password:password},
success : function(data){
if(data == true){
this.setState({sessionFlag:true})
$('#home')[0].click();
}
else {
this.setState({sessionFlag:false})
}
}.bind(this)
})
}
render(){
const {name,password} = this.state;
return (
<Router>
<div>
<Route path="/login" exact component={()=><Login
onClickHandle={this.login.bind(this)}
onChangeName={this.changeName.bind(this)}
onChangePass={this.changePass.bind(this)}
name={name}
password = {password} />} />
</div>
</Router>
)
}
login.js
render(){
return (
<form className="form-horizontal" method ="post">
<input
type="text"
onChange={this.props.onChangeName}
value={this.props.name}/>
<input type="text"
onChange={this.props.onChangePass}
value={this.props.password} />
<input type="button"
value="Login"
onClick={this.props.onClickHandle} />
</form>
)
}
The main issue is the manner in which you are specifying your Login component:
<Route
path="/login"
exact
component={() => (
<Login
onChangeName={this.changeName.bind(this)}
onChangePass={this.changePass.bind(this)}
name={this.state.name}
password={this.state.password}
/>
)}
/>
Using this syntax causes the child of the Route to look like a brand-new type of component with each rendering (since it will be a new arrow function instance each time) so the previous Login component will be completely unmounted and the new one mounted.
From https://reactrouter.com/web/api/Route/component:
When you use component (instead of render or children, below) the router uses React.createElement to create a new React element from the given component. That means if you provide an inline function to the component prop, you would create a new component every render. This results in the existing component unmounting and the new component mounting instead of just updating the existing component. When using an inline function for inline rendering, use the render or the children prop (below).
Here is an example using the render-func approach:
Header.js
import React from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import Login from "./Login";
class Header extends React.Component {
constructor(props) {
super(props);
this.state = { name: "", password: "" };
this.changeName = this.changeName.bind(this);
this.changePass = this.changePass.bind(this);
}
changeName = (e) => {
this.setState({ name: e.target.value });
};
changePass = (e) => {
this.setState({ password: e.target.value });
};
render() {
return (
<Router>
<div>
<div>
<Link to="/login">Login</Link>
</div>
<Route
path="/login"
exact
render={() => (
<Login
onChangeName={this.changeName}
onChangePass={this.changePass}
name={this.state.name}
password={this.state.password}
/>
)}
/>
</div>
</Router>
);
}
}
export default Header;

react js authentication best practise

how can I prevent react routes to be called when a user is not logged in?
Want a single page with a login form be displayed whenn / is called.
every other route should be callable only if a user is logged in.
thanks
index.js
const render = () => {
ReactDOM.render(
<Provider store={store}>
<AppContainer>
<Router history={createHistory}>
<div>
<div className={t.topBar}>
<TopBarHelper/>
</div>
<div className={css.sidebararound}>
</div>
<Route exact path="/" component={Login}/>
<PrivateRoute path="/dashboard" component={Dashboard}/>
....
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!</p>
) : (
<p>You are not logged in.</p>
)
))
The login is in a separate file and looks like this:
class Login extends Component {
constructor(props) {
super(props);
this.changeHandler = this.changeHandler.bind(this);
this.state = {
activeSnackbar: false,
snackbarText: '',
redirectToReferrer: false
};
}
componentDidMount() {
this.getData()
};
/**
* Fetches all Data needed for this view
*/
getData() {
console.log(this.props)
};
/**
* Handle Change
* #param field
* #param value
*/
changeHandler(value, {target: {name, type}}) {
this.setState({
[name]: type === 'number' ? parseInt(value, 10) : value,
});
}
/**
* Submit new Ticket Form
* #param event
*/
handleLogin = (event) => {
this.context.router.push('/dashboard');
// axios.post(API_URL + '/dashboard')
// .then((response) => {
// })
// .catch(function (error) {
// console.log(error);
// });
};
render() {
return (
<div>
<div className={css.loginpanel}>
<div className={css.loginpanellogo}>
<img src={logo} width="200px"/>
</div>
<div className={css.loginpanelform}>
<h1><T value="user.login.welcome"/></h1>
<form id="login" method="POST" action="">
<Input type='email' label={<T value='user.login.email'/>} name='email' value={this.state.email} onChange={this.changeHandler}/>
<Input type='password' label={<T value='user.login.password'/>} name='password' value={this.state.password} onChange={this.changeHandler}/>
<br />
<Button icon='save' onClick={this.AuthButton} label={<T value="user.login.signin"/>} raised primary/>
<Button label={<T value="user.login.signup"/>} raised/>
</form>
<br/>
</div>
</div>
</div>
);
}
}
export default connect()(Login);
But I can't call the AuthButton function from Login
It could look like this with react-router-4
<PrivateRoute path="/protected" component={Protected}/>
and
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={props =>
fakeAuth.isAuthenticated
? <Component {...props}/>
: <Redirect to={{
pathname: '/login',
state: { from: props.location }
}}/>
)}/>
)
The basic gist is that you intercept the transition, check for authentication and redirect accordingly.
See full example here.
you can use two options
you can check whether user is logged in or not on
<Route onChange={requireLogin}></Route>
Or you can check on individual route like
<Route onEnter={requireLogin}/>
/>

Categories