can I create a custom route without using redux? - javascript

I have a Loginscreen.js
import React, { Component } from 'react';
import Login from './Login';
class Loginscreen extends Component {
constructor(props){
super(props);
this.state={
username:'',
password:'',
loginscreen:[],
loginmessage:'',
isLogin:true
}
}
componentWillMount(){
var loginscreen=[];
loginscreen.push(<Login parentContext={this} appContext={this.props.parentContext}/>);
this.setState({
loginscreen:loginscreen
})
}
render() {
return (
<div className="loginscreen">
{this.state.loginscreen}
</div>
);
}
}
const style = {
margin: 15,
};
export default Loginscreen;
and I have Login.js
import React, { Component } from 'react';
import { Button, Form, Grid, Segment } from 'semantic-ui-react'
import axios from 'axios';
import validator from 'Validator';
import InlineError from './components/messages/InlineError';
import UploadScreen from './UploadScreen';
import { BrowserRouter as Router, Route } from 'react-router-dom';
class Login extends Component {
constructor(){
super();
this.state={
email:'',
password:'',
errors: {}
}
}
onChange = (e) => {
this.setState({ [e.target.name]: e.target.value });
}
validate = () => {
if (!this.state.password) this.state.errors.password = "Can't be blank";
if (!this.state.email) this.state.errors.email = "Invalid Email";
return this.state.errors;
}
onSubmit = (e) => {
const errors = this.validate(this.state);
this.setState({ errors });
e.preventDefault();
var apiBaseUrl = "http://127.0.0.1:8000/api/auth/login";
var self = this;
var payload={
"email":this.state.email,
"password":this.state.password
}
var config = {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
withCredentials: false
}
axios.post(apiBaseUrl, payload, config)
.then(function (response) {
console.log(response);
if(response.status == 200){
console.log("Login successful");
}
else if(response.status == 204){
console.log("Username password do not match");
alert("username password do not match")
}
else{
console.log("Username does not exists");
alert("Username does not exist");
}
})
.catch(function (error) {
console.log(error);
});
}
render() {
const { email, password, errors } = this.state;
return (
<div className='login-form'>
<style>{`
body > div,
body > div > div,
body > div > div > div.login-form {
height: 100%;
}
`}</style>
<Grid textAlign='center' style={{ height: '100%' }} verticalAlign='middle'>
<Grid.Column style={{ maxWidth: 450 }}>
<br />
<br />
<Form size='large' onSubmit={this.onSubmit}>
<Segment stacked>
<Form.Input
type='text'
name='email'
value={email}
onChange={this.onChange}
fluid
icon='user'
iconPosition='left'
placeholder='E-mail address'
/>
{ errors.email && <InlineError text={errors.email} />}
<Form.Input
type='password'
name='password'
value={password}
onChange={this.onChange}
fluid
icon='lock'
iconPosition='left'
placeholder='Password'
/>
{ errors.password && <InlineError text={errors.password} />}
<Button fluid size='large' type="submit">
Login
</Button>
</Segment>
</Form>
</Grid.Column>
</Grid>
</div>
);
}
}
const style = {
margin: 15,
};
export default Login;
that returns:
can I use this response alone to be able to create a conditional statement that would create a custom route that can only be accessed if someone is logged in successfully?
this is my app.js
import React from 'react';
import { Route } from 'react-router-dom';
import LoginPage from "./Login";
import UploadScreen from "./UploadScreen";
const App = () => (
<div className="ui container">
<Route path="/" exact component={LoginPage} />
<Route path="/upload" exact component={UploadScreen} />
</div>
);
export default App;
I want that "/upload" route to only be accessible if someone is logged in. can I do that?

Here's a way to make public and private routes:
const PrivateRoute = ({ component: Component, ...rest, loggedIn }) => (
<Route
{...rest}
render={props =>
(loggedIn ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: LOGIN,
state: { from: props.location },
}}
/>
))
}
/>
);
const PublicRoute = ({ component: Component, ...rest, loggedIn}) => (
<Route
{...rest}
render={props =>
(!loggedIn ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: HOME,
state: { from: props.location },
}}
/>
))
}
/>
)

Related

Pass props to a component from another component, React Router v4

I'm trying to pass props to a component from another component with React Router v4, but I'm not getting the result I want.
This is how I'm trying to pass props:
render() {
const { selectedLanguage, repos, error, dashboard } = this.state
return (
<React.Fragment>
<LanguagesNav
selected={selectedLanguage}
onUpdateLanguage={this.updateLanguage}
/>
{error && <p>{error}</p>}
{repos && <LoginForm repos={repos} selected={selectedLanguage} dashboard={dashboard} onUpdateLogin={this.updateLogin} />}
<Route
path='/dashboard'
render={(props) => (
<Dashboard {...props}
repos={repos}
selected={selectedLanguage}
/>
)}
/>
</React.Fragment>
The whole component I want to send props from:
Login.js
import React from 'react'
import PropTypes from 'prop-types'
import languagesdata from '../languagesdata.json'
import { fetchLanguageRepos } from '../utils/api'
import Dashboard from './Dashboard'
import {BrowserRouter as Router, Route } from 'react-router-dom'
import { Link } from 'react-router-dom'
function LanguagesNav ({ selected, onUpdateLanguage}) {
const languages = ['EU', 'ES', 'EN']
return (
<div >
<h1 className='center-text header-lg'>
GAUR 2.0
</h1>
<ul className='flex-center'>
{languages.map((language) => (
<li key={language}>
<button
className='btn-clear nav-link'
style={language === selected ? { color: 'rgb(187, 46, 31)' } : null }
onClick={() => onUpdateLanguage(language)}>
{language}
</button>
</li>
))}
</ul>
</div>
)
}
LanguagesNav.propTypes = {
selected: PropTypes.string.isRequired,
onUpdateLanguage: PropTypes.func.isRequired
}
function LoginForm ({ repos, selected, dashboard, onUpdateLogin }) {
var language = {}
switch (selected) {
case "EU":
selected = "EU";
language = repos[0].terms;
break;
case "ES":
selected = "ES";
language = repos[1].terms;
break;
case "EN":
selected = "EN";
language = repos[2].terms;
break;
}
return (
<form className='column player'>
<div className='row player-inputs'>
<input
type='text'
id='username'
className='input-light'
placeholder={language.username}
autoComplete='off'
/>
</div>
<div className='row player-inputs'>
<input
type='password'
id='password'
className='input-light'
placeholder={language.password}
autoComplete='off'
/>
</div>
<div className='row player-inputs'>
<Link
className='btn dark-btn'
to={{
pathname: '/dashboard',
}}
>
{language.login}
</Link>
</div>
</form>
)
}
LoginForm.propTypes = {
repos: PropTypes.array.isRequired
}
export default class Login extends React.Component {
constructor(props) {
super(props)
this.state = {
selectedLanguage: 'EU',
repos: null,
error: null,
dashboard: false
}
this.updateLanguage = this.updateLanguage.bind(this)
}
componentDidMount () {
this.updateLanguage(this.state.selectedLanguage)
}
updateLanguage (selectedLanguage) {
this.setState({
selectedLanguage,
error: null
})
fetchLanguageRepos(selectedLanguage)
.then((repos) => this.setState({
repos,
error: null,
}))
.catch(() => {
console.warn('Error fetching repos: ', error)
this.setState({
error: 'There was an error fetching the repositories.'
})
})
}
render() {
const { selectedLanguage, repos, error, dashboard } = this.state
return (
<React.Fragment>
<LanguagesNav
selected={selectedLanguage}
onUpdateLanguage={this.updateLanguage}
/>
{error && <p>{error}</p>}
{repos && <LoginForm repos={repos} selected={selectedLanguage} dashboard={dashboard} onUpdateLogin={this.updateLogin} />}
<Route
path='/dashboard'
render={(props) => (
<Dashboard {...props}
repos={repos}
selected={selectedLanguage}
/>
)}
/>
</React.Fragment>
)
}
}
And this is the component I want to send props to:
Dashboard.js
import React from 'react'
import PropTypes from 'prop-types'
import Card from './Card'
import Loading from './Loading'
import { fetchLanguageRepos } from '../utils/api'
import Profile from './Profile'
import { Link } from 'react-router-dom'
function ReposGrid ({ repos, selected, profile, onUpdateProfile }) {
var language = {}
switch (selected) {
case "EU":
selected = "EU";
language = repos[0].terms;
break;
case "ES":
selected = "ES";
language = repos[1].terms;
break;
case "EN":
selected = "EN";
language = repos[2].terms;
break;
}
return (
<ul className='grid space-around'>
<li key={language.studyplan}>
<button
onClick={() => {onUpdateProfile(`${profile}`)}}
>
<Card
header={language.studyplan}
>
</Card>
</button>
</li>
<li key={language.careers}>
<Link to='/'>
<Card
header={language.careers}
>
</Card>
</Link>
</li>
<li key={language.census}>
<Card
header={language.census}
>
</Card>
</li>
<li key={language.sms}>
<Card
header={language.sms}
>
</Card>
</li>
<li key={language.tuitions}>
<Card
header={language.tuitions}
>
</Card>
</li>
<li key={language.surveys}>
<Card
header={language.surveys}
>
</Card>
</li>
<li key={language.titles}>
<Card
header={language.titles}
>
</Card>
</li>
<li key={language.exams}>
<Card
header={language.exams}
>
</Card>
</li>
<li key={language.record}>
<Card
header={language.record}
>
</Card>
</li>
</ul>
)
}
export default class Dashboard extends React.Component {
constructor(props) {
super(props)
this.state = {
selectedLanguage: 'EU',
repos: null,
error: null,
loading: true,
profile: false
}
this.updateProfile = this.updateProfile.bind(this)
}
componentDidMount () {
var { repos, selectedLanguage } = this.props
this.setState({
selectedLanguage,
repos,
error: null,
loading: false,
profile: false
})
}
updateProfile (profile) {
this.setState({
error: null,
profile: true
})
}
render() {
const { selectedLanguage, repos, error } = this.props
const { profile } = this.state
if (profile === true) {
return (
<React.Fragment>
{repos && <Profile repos={repos} selectedLanguage={selectedLanguage} />}
</React.Fragment>
)
}
return (
<React.Fragment>
{repos && <ReposGrid repos={repos} selected={selectedLanguage} profile={profile} onUpdateProfile={this.updateProfile} />}
</React.Fragment>
)
}
}
I should be getting something like this:
Instead, I'm getting this:
Just to give more information, this is my index.js where I have set a route for Login.js:
index.js
import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import Login from './components/Login'
import Dashboard from './components/Dashboard'
import Nav from './components/Nav'
import {BrowserRouter as Router, Route } from 'react-router-dom'
class App extends React.Component {
render() {
return (
<Router>
<div className='container'>
<Nav />
<Route exact path='/' component={Login} />
</div>
</Router>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('app')
)
I've tried setting the route in index.js, but it didn't work either. What's the correct way to do it?

ReferenceError: fullName is not defined

I have to create is a registration form that consists of full name, email, password, mobile number, DOB and once the email validation is done and once the submit button is clicked, it should direct me to the login page. The login page has an email and password (the details from registration need not match the login) and once the button is clicked(after the validation) it should display "Welcome". I'm sharing the files here. Used, react-router, Material UI.
Login.js
/* eslint-disable no-undef */
import React, { Component } from "react";
import { Link } from "react-router-dom";
import { Grid, Paper, TextField } from "#material-ui/core";
import { Button } from "#material-ui/core";
import Welcome from './Welcome';
class Login extends Component {
constructor(props) {
super(props);
this.state = {
email: "",
password: "",
errors: {},
};
this.validate = this.validate.bind(this);
}
validateEmail(email) {
const re = /^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(email);
}
validate() {
const { email, password, errors } = this.state;
if (!this.validateEmail(email)) {
errors.email = "Provide valid email";
}
if (password.length > 5) {
errors.password = "Provide min 5-digit password";
}
if (errors) {
this.setState({ errors });
} else {
this.props.location.push('/Welcome');
}
}
render() {
const paperStyle = {
padding: 20,
height: "70vh",
width: 280,
margin: "20px auto",
};
return (
<Grid>
<Paper elevation={10} style={paperStyle}>
<Grid align="center">
<h2>Login</h2>
</Grid>
<TextField
error={errors.email}
label="Email"
placeholder="Enter mail"
fullwidth
value={email}
onChange={(e) => this.setState({ email: e.target.value })}
required
helperText={errors.password}
/>
<TextField
error={errors.password}
label="Password"
placeholder="Enter password"
type="password"
fullwidth
required
value={password}
onChange={(e) => this.setState({ password: e.target.value })}
helperText={errors.password}
/>
{/*<Link to="/Welcome">*/}
<Button variant="contained" color="primary" onClick={this.validate}>
Login
</Button>
{/*</Link> */}
</Paper>
</Grid>
);
}
}
export default Login;
Registration.js
/* eslint-disable no-undef */
import React, { Component} from 'react';
import { Link } from "react-router-dom";
import { Grid, Paper, TextField } from "#material-ui/core";
import { Button } from "#material-ui/core";
import Login from './Login';
class Registration extends Component {
constructor(props) {
super(props);
this.state = {
fullName: "",
mobileNumber: "",
email: "",
password: "",
errors: {},
};
this.validate = this.validate.bind(this);
}
validateEmail(email) {
const re = /^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(email);
}
validate() {
const { fullName, email, password, errors } = this.state;
if (!this.validateEmail(email)){
errors.email = "Provide valid email";
}
if (password.length > 5){
errors.password = "Provide min 5-digit password";
}
if (errors) {
this.setState({ errors });
} else {
this.props.location.push('/Login');
}
}
render() {
const paperStyle = {
padding: 20,
height: "70vh",
width: 280,
margin: "20px auto",
};
return (
<Grid>
<Paper elevation={10} style={paperStyle}>
<Grid align="center">
<h2>Login</h2>
</Grid>
<TextField
label="Full name"
placeholder="Enter name"
fullwidth
value={fullName}
onChange={(e) => this.setState({ fullName: e.target.value })}
required
helperText={errors.fullName}
/>
<TextField
type = 'number'
label="Mobile number"
placeholder="Enter mobile number"
fullwidth
// eslint-disable-next-line no-undef
value={mobileNumber}
onChange={(e) => this.setState({ mobileNumber: e.target.value })}
required
helperText={errors.mobileNumber}
/>
<TextField
error={errors.email}
label="Email"
placeholder="Enter mail"
fullwidth
value={email}
onChange={(e) => this.setState({ email: e.target.value })}
required
helperText={errors.email}
/>
<TextField
error={errors.password}
label="Password"
placeholder="Enter password"
type="password"
fullwidth
required
value={password}
onChange={(e) => this.setState({ password: e.target.value })}
helperText={errors.password}
/>
{/*<Link to="/Login">*/}
<Button variant="contained" color="primary" onClick={this.validate}>
Register here
</Button>
{/*</Link>*/}
</Paper>
</Grid>
);
}
}
export default Registration;
App.js
import React, { Component } from "react";
import "./App.css";
import Login from "./Login";
import Registration from "./Registration";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import Welcome from "./Welcome";
import { Typography } from "#material-ui/core";
class App extends Component {
render() {
return (
<Router>
<Typography>
<div className="App">
<Switch>
<Route path="/" exact component={Registration} />
<Route path="/Login" component={Login} />
<Route path="/Welcome" component={Welcome} />
</Switch>
</div>
</Typography>
</Router>
);
}
}
export default App;
Welcome js
import React from "react";
function Welcome() {
return <h1>Welcome</h1>;
}
export default Welcome;
You need add const { fullName, email, password, errors } = this.state; in the render and before return

Component not getting mounted using Redirect To of React Router

Even though, I'm able to go the route, the Login component associated with the /login route is not getting mounted. There is code for a total of 5 components (App, Login, Auth, Layout3, TopBar) which I've added below. For the Auth component, I'm making use of the 'react-token'auth' package. Please let me know if you have any questions, the code might seem a lot. But you can focus on the code for the TopBar component as that's where I'm redirecting to the login route. And of course, in the App component, I've added the route for Login. So the TopBar and App component is where there might be a problem.
Here is the App component where I have defined all the routes for my application.
class App extends Component {
render() {
<StylesProvider injectFirst>
<div>
<BrowserRouter>
<Switch>
<Route exact path='/login' component={Login} />
<Route exact path='/' component={Layout3} />
</Switch>
</BrowserRouter>
</div>
</StylesProvider>
}
Now, here is the Login component. Here, if the user is logged in, he gets redirected to the '/' route which is the Layout3 component. But if the person isn't logged in, he'll get shown the Login page. I've added the logic for that in the return. UseAuth() points to the Auth.js file where I've imported the createAuthProvider from react-token-auth package.
export default function Login(props) {
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const [loggedIn, setLoggedIn] = useState(false)
const onSubmitClick = (e) => {
e.preventDefault()
console.log("You pressed login")
let opts = {
'username': username,
'password': password
}
console.log(opts)
fetch('/api/login', {
method: 'post',
body: JSON.stringify(opts)
}).then(r => r.json())
.then(token => {
if (token.access_token) {
login(token)
// setLoggedIn(true)
console.log(token)
}
else {
console.log("Please type in correct username/password")
}
})
}
const handleUsernameChange = (e) => {
setUsername(e.target.value)
}
const handlePasswordChange = (e) => {
setPassword(e.target.value)
}
const [logged] = useAuth();
console.log('logged or not: ', logged);
return (
<div style={{
backgroundColor: 'white', height: '100%', display: 'flex', flexDirection: 'column',
alignItems: 'center'
}}>
{!logged ?
<>
<h1>Please Log In</h1>
<form>
<label>
<p>Username</p>
<input type="text"
onChange={handleUsernameChange}
/>
</label>
<label>
<p>Password</p>
<input type="password"
onChange={handlePasswordChange}
/>
</label>
<div>
<button
type="submit"
onClick={onSubmitClick}
>Submit</button>
</div>
</form>
</>
:
<>
<Redirect to='/' push={true} />
</>
}
</div>
)
}
Here is the Auth.js file that I was talking about.
import { createAuthProvider } from 'react-token-auth';
export const [useAuth, authFetch, login, logout] =
createAuthProvider({
accessTokenKey: 'access_token',
onUpdateToken: (token) => fetch('/api/refresh', {
method: 'POST',
body: token.access_token
})
.then(r => r.json())
});
Here is the Layout3 component where I'm conditionally going to either the Layout3 comp or the Login comp.
class Layout3 extends Component {
state = {
authMessage: '',
loginStatus: false
}
componentDidMount() {
authFetch("/api/protected").then(response => {
console.log('response inside authenticationFetch: ', response)
if (response.status === 401) {
this.setState({ authMessage: response.status })
return null
}
return response.json()
}).then(response => {
if (response && response.message) {
this.setState({ authMessage: response.message })
}
})
}
render() {
return (
<>
{
this.state.authMessage === 401 ?
<>
<BrowserRouter>
<Redirect to='/login' />
</BrowserRouter>
</>
:
<>
<BrowserRouter>
<Switch>
<Route exact path='/'>
<TopBar/>
</Switch>
</BrowserRouter>
</>
}
</>...
And finally, here is the TopBar where the logout button is present.
class TopBar extends Component {
state = {
loggedOut: false
}
dashboardLogOut = () => {
this.setState({ loggedOut: true });
}
render() {
console.log('state inside topbar ', this.state.loggedOut)
if (this.state.loggedOut === true) {
logout()
return (
<Redirect from="/" to="/login" push={true} />
)
}
return(
<ul class="nav navbar-top-links navbar-right">
<li>
<button onClick={this.dashboardLogOut}>
<i class="fa fa-sign-out"></i> Log out
</button>
</li>
</ul>
);
}
}
You cannot export like this export const [useAuth, authFetch, login, logout]
It must be a defined variable
import { createAuthProvider } from "react-token-auth";
export const useAuthProvider = createAuthProvider({
accessTokenKey: "access_token",
onUpdateToken: (token) =>
fetch("/api/refresh", {
method: "POST",
body: token.access_token
}).then((r) => r.json())
});
And later you can use it with a destructuring call in Login.js
const [useAuth, authFetch, login, logout] = useAuthProvider();
const [logged] = useAuth();
Also Layout3 doesn't look good because it breaks single responsibility principle. Condition based redirection must be implemented on a router level

instance.render is not a function. (functional component)

This is my first post.
I've been reading a lot about functional components and trying everything that I could, but nothing seems to work in my case. I am getting instance.render is not a function.
Hopefully some of you can see where my error is, as I am quite new to programming. This is my code:
App.js
import React, { useEffect, useState } from 'react';
import './App.css';
import Login from "./components/Login"
import Feed from "./components/Feed"
import { BrowserRouter as Router, Route, Switch } from "react-router-dom"
const App = () => {
const [isLoggedIn, setLoggedIn] = useState(false)
const handleLogin = token => {
if (!token) return
localStorage.setItem('token', token)
setLoggedIn(true)
}
const handleLogout = () => () => {
setLoggedIn(false)
localStorage.clear()
}
return (
<div className="App">
<Router>
<Feed isLoggedIn={isLoggedIn} logout={handleLogout} />
<Switch>
<Route
exact
path='/login'
component={(props) => (
<Login {...props} onLogin={handleLogin} />
)}>
</Route>
</Switch>
</Router>
</div>
)
}
export default App;
Login.js
import React from "react"
import axios from "axios"
import { Button, Form, FormGroup, Input } from 'reactstrap'
import "../css/Login.css"
import { Link, BrowserRouter as Router, Switch, Route } from "react-router-dom"
import Signin from "./Signin"
class Login extends React.Component {
constructor (props) {
super(props)
this.state = {
user_name: "",
password: "",
error: false,
loggedIn: false,
}
}
handleChange = (e) => {
this.setState({
[e.target.name]: e.target.value,
})
}
login = (event) => {
event.preventDefault()
const { user_name, password } = this.state
axios("http://localhost:7001/api/login", {
method: "POST",
data: {
user_name,
password
}
})
.then((response) => {
this.props.onLogin(response.data.token)
this.setState({ loggedIn: true })
this.feedRedirect()
console.log(response.data)
})
.catch((error) => {
console.log(error)
})
this.setState({
user_name: "",
password: "",
error: false,
})
}
feedRedirect = () => {
this.props.history.push('/feed')
}
render() {
const { user_name, password, error, loggedIn } = this.state
return (
<div className="login">
<Form className="login-container" onSubmit={this.login}>
<FormGroup>
<Input
value={this.state.user_name}
onChange={this.handleChange}
name="user_name"
type="text"
className="form-control mb-2"
placeholder="Username"
/>
</FormGroup>
<FormGroup>
<Input
value={this.state.password}
onChange={this.handleChange}
name="password"
type="password"
className="form-control mb-2"
placeholder="Password"
/>
</FormGroup>
<Button className="button-login" disabled={!user_name || !password}>
Log in
</Button>
<hr />
<Router>
<Link to="/signin"> Don't have an account? Sign up here</Link>
<Switch>
<Route path="/signin" component={Signin}>
</Route>
</Switch>
</Router>
</Form>
</div>
)
}
}
export default Login;
Any help will be appreciated. Thank you in advance.

React router renders component after a refresh

This is a strange issue, but when I try to do a redirect using a link, nothing happens, just the URL changes. But when I refresh the browser the component gets rendered. What am I doing wrong?
My nav.js
import React from 'react';
import {Navbar, Nav, NavItem, Modal, Button, FormControl} from 'react-bootstrap';
import {BrowserRouter, Link, Route, Switch} from 'react-router-dom';
import {auth} from '../firebase';
import Questions from './questions';
import {About} from './about';
import {Home} from './home';
import {LinkContainer} from 'react-router-bootstrap';
import Question from "./question";
class Navigation extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.login = this.login.bind(this);
this.logout = this.logout.bind(this);
this.openLogin = this.openLogin.bind(this);
this.handleClose = this.handleClose.bind(this);
}
componentDidMount() {
auth.onAuthStateChanged((user) => {
if (user) {
this.setState({
user: user
}, function () {
this.props.checkUserState(this.state.user)
});
}
});
}
logout() {
auth.signOut()
.then(() => {
this.setState({
user: null
}, function () {
this.props.checkUserState(this.state.user)
});
});
}
login() {
var email = document.getElementById('email').value;
var password = document.getElementById('password').value;
auth.signInWithEmailAndPassword(email, password)
.then(result => {
const user = result.user;
this.setState({
user: user,
},
function () {
this.props.checkUserState(this.state.user)
});
document.getElementById('close').click();
document.getElementById('questions').click();
}
).catch(e => console.log(e));
}
openLogin() {
this.setState({show: true});
}
handleClose() {
this.setState({show: false});
}
render() {
return (
<React.Fragment>
<BrowserRouter>
<React.Fragment>
<Navbar>
<Navbar.Header>
<Navbar.Brand>
<Link id='home' to="/">UczIchApp</Link>
</Navbar.Brand>
</Navbar.Header>
<Nav>
<LinkContainer id='about' to='/about'>
<NavItem>O nas</NavItem>
</LinkContainer>
{
this.state.user ?
<React.Fragment>
<LinkContainer id="questions" to='/questions'>
<NavItem>Zadania</NavItem>
</LinkContainer>
<NavItem onClick={this.logout}>Wyloguj się</NavItem>
</React.Fragment>
:
<NavItem onClick={this.openLogin}>Zaloguj się</NavItem>
}
</Nav>
</Navbar>
<Switch>
<Route exact path="/about" component={About}/>
<Route exact path="/questions" component={Questions}/>
<Route exact path="/" component={Home}/>
<Route path='/question/:id' component={Question}/>
</Switch>
</React.Fragment>
</BrowserRouter>
<Modal
show={this.state.show}
onHide={this.handleClose}>
<Modal.Header
closeButton>
<Modal.Title> Modal
heading </Modal.Title>
</Modal.Header>
<Modal.Body>
<form>
<FormControl
id="email"
type="email"
label="Email address"
placeholder="Enter email"/>
<FormControl id="password" label="Password" type="password"/>
<Button onClick={this.login}>Zaloguj</Button>
</form>
</Modal.Body>
<Modal.Footer>
<Button id="close" onClick={this.handleClose}>Close</Button>
</Modal.Footer>
</Modal>
</React.Fragment>
)
}
}
export default Navigation;
My Questions.js
import React from 'react';
import firebase from 'firebase';
// import {Button} from 'react-bootstrap';
import {BrowserRouter as Router, Link, Route} from 'react-router-dom';
import Question from './question';
class Questions extends React.Component {
constructor(props) {
super(props);
this.state = {
currentItem: '',
username: '',
questions: []
};
}
componentDidMount() {
const questionsRef = firebase.database().ref('Works').orderByChild('available').equalTo(true).limitToFirst(10);
questionsRef.on('value', (snapshot) => {
let questions = snapshot.val();
let newState = [];
for (let question in questions) {
newState.push({
id: question,
category: questions[question].category,
level: questions[question].level,
pointAmount: questions[question].pointAmount,
pointBoost: questions[question].pointBoost,
photoURL: questions[question].photoURL,
});
}
this.setState({
questions: newState
});
});
}
render() {
return (
<section id='loopContainer' className='display-question'>
<div className='wrapper'>
<ul style={{listStyleType: 'none'}}>
{
this.state.questions.map(question => {
return (
<li key={question.id}>
<h3>Kategoria: {question.category}</h3>
<p>Poziom: {question.level}</p>
<p>Punkty: {question.pointAmount + question.pointBoost}</p>
<img alt='' style={{width: '20%'}} src={question.photoURL}/>
<Router>
<React.Fragment>
<Link to={`/question/${question.id}`}
style={{display: 'block', margin: 'auto'}}>Rozwiaz to zadanie
</Link>
</React.Fragment>
</Router>
</li>
)
})
}
</ul>
</div>
</section>
)
}
}
export default Questions;
My Question.js
import React from 'react';
import firebase from 'firebase';
export default class Question extends React.Component {
constructor(p) {
super(p);
this.state = {
currentItem: '',
username: '',
questions: []
};
}
componentDidMount() {
const questionsRef = firebase.database().ref('Works').orderByChild('firebaseKey').equalTo(this.props.match.params.id);
questionsRef.on('value', (snapshot) => {
let questions = snapshot.val();
let newState = [];
for (let question in questions) {
newState.push({
id: question,
category: questions[question].category,
level: questions[question].level,
pointAmount: questions[question].pointAmount,
pointBoost: questions[question].pointBoost,
photoURL: questions[question].photoURL,
});
}
console.log(newState);
this.setState({
questions: newState
});
});
}
render() {
return (
this.state.questions.map(question => {
return (
<section key={question.id} className='display-question'>
<div className='wrapper'>
<h3>Kategoria: {question.category}</h3>
<p>Poziom: {question.level}</p>
<p>Punkty: {question.pointAmount + question.pointBoost}</p>
<img alt='' style={{width: '80%'}} src={question.photoURL}/>
</div>
</section>
)
})
)
}
}
What I'm trying to do. I'm trying to get a Question component rendered, when a link rendered with the Questions component is clicked. Based on the id, the Question component will differ.
This is the Questions component example (It's a list):
When I click the link under an image it changes the url, like this: http://localhost:3000/question/-LDvDwsIrf_SCwSinpMa, but nothing else happens. I have to manually refresh the page to get the component.
This is the single Question component being rendered
What am I missing?
You define multiple Router instances. However, there should be exactly one Router instance. The Router is typically placed pretty much "top-level" within your App / Main / Root component.

Categories