change login to logout when login - javascript

i'm following a tutorial to make the navbar changes based if the token is stored in the localstorage or not the problem that when i'm logged , the log in link still in the navbar , but i want to have logout
here is my code :
eventbus.js
<script>
import Vue from 'vue'
const EventBus = new Vue()
export default EventBus
</script>
navbar.vue
<template>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark rounded">
<button class="navbar-toggler"
type="button"
data-toggle="collapse"
data-target="#navbar1"
aria-controls="navbar1"
aria-expanded="false"
aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse justify-content-md-center" id="navbar1">
<ul class="navbar-nav">
<li class="nav-item">
<router-link class="nav-link" to="/">Home</router-link>
</li>
<li v-if="auth==''" class="nav-item">
<router-link class="nav-link" to="/login">Login</router-link>
</li>
<li v-if="auth==''" class="nav-item">
<router-link class="nav-link" to="/register">Register</router-link>
</li>
<li v-if="auth=='loggedin'" class="nav-item">
<router-link class="nav-link" to="/profile">Profile</router-link>
</li>
<li v-if="auth=='loggedin'" class="nav-item">
<a class="nav-link" href="" v-on:click="logout">Logout</a>
</li>
</ul>
</div>
</nav>
</template>
<script>
import EventBus from './EventBus'
EventBus.$on('logged-in', test => {
console.log(test)
})
export default {
data () {
return {
auth: '',
user: ''
}
},
methods: {
logout () {
localStorage.removeItem('usertoken')
}
},
mounted () {
EventBus.$on('logged-in', status => {
this.auth = status
})
}
}
</script>
login.vue
<template>
<div class="login-page">
<div class="form">
<form #submit.prevent="login">
<input type="text" name="username" v-model="username" placeholder="Username" />
<input type="password" name="password" v-model="password" placeholder="Password" />
<input type="submit" value="LOG IN">
</form>
</div>
</div>
</template>
<script>
import axios from 'axios'
import router from '../router/index'
import EventBus from '../components/EventBus'
import VueCookies from 'vue-cookies'
export default {
data () {
return {
username: '',
password: ''
}
},
methods: {
login () {
axios.post('http://localhost:3001/login',
{
username: this.username,
password: this.password
}
).then((res) => {
console.log(res.data.token)
if(res.data.status===false){
router.push({ name: 'login' })
} else{
localStorage.setItem('usertoken', res.data.token)
this.username = ''
this.password = ''
router.push({ name: 'showgames'})
}
}).catch((err) => {
console.log(err)
})
this.emitMethod()
},
emitMethod () {
EventBus.$emit('logged-in', 'loggedin')
}
}
}
</script>
i would like to know why the navbar didnt change , thank you for your help

The this.emitMethod() statement should be inside the body of .then(...) and yours is outside.
It is also a good practice to use a beforeDestroy() hook in navbar.vue to remove the event handler or otherwise you will have multiple instances of the handler on multiple component mountings (which is quite often during the development).

Related

setState not working in ReactJS GET method

I've been trying to make a navbar for my website that handles log in and logout authentication. To show a log in or logout button based on if the user is either. I have sucessfully retrieved the data I need but I am having trouble rendering and saving it in my Navbar component, it looks like this:
import React from 'react';
class Navbar extends React.Component {
constructor(props){
super(props)
this.state = { loggedin: ''}
this.isLoggedIn = this.isLoggedIn.bind(this)
}
componentDidMount(){
fetch('/user-status', {method: 'GET'}).then(res => res.json()).then(data =>{
this.setState({loggedin: data})
});
}
isLoggedIn(){
fetch('/user-status',{ method: 'GET'})
.then(function (response) {
return response.json();
}).then(function (textie) {
this.setState({loggedin: textie})
return textie;
});
}
render() {
if(this.isLoggedIn == 'true'){
return(
<a className="nav-item nav-link" href="/logout">Logout</a>
);
}
return (
<nav className="navbar navbar-expand-lg navbar-dark bg-primary">
<a className="navbar-brand" href="#">Navbar</a>
<button className="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
<span className="navbar-toggler-icon"></span>
</button>
<div className="collapse navbar-collapse" id="navbarNavAltMarkup">
<div className="navbar-nav">
<a className="nav-item nav-link active" href="#">Home <span className="sr-only">(current)</span></a>
<a className="nav-item nav-link" href="/about">About</a>
<a className="nav-item nav-link" href="/signin">Sign in/Sign Up</a>
<a className="nav-item nav-link" href="/profile">My Profile</a>
<a className="nav-item nav-link" href="/leaderboard">Top Forecasts</a>
</div>
</div>
</nav>
);
}
}
export default Navbar;
The /user-status method gets whether or not the user is logged in from Flask backend, and returns either true or false. I try to save it once it changes in isLoggedIn, but I get the error cannot 'setState' on undefined. Also, when it returns textie from isLoggedIn, it returns the statement 'bound isLoggedIn' instead of true or false, and I'm not sure why.
I try to say in the render part that if the loggedin variable is true, there will be a Navbar option to logout, but if there isnt, there will be a navbar option to log in. Any thoughts why this isn't working and this error keeps popping up? I guess what I'd like to know is how to save a fetched data into a react component.
In this line if(this.isLoggedIn == 'true') you are checking if the function isLoggedIn in the this scope is equals to true, but you actually should call to this.state.isloggedIn == 'true' I think.
Try this:
class Navbar extends React.Component {
state = { loggedin: '' };
componentDidMount() {
this.isLoggedIn();
}
isLoggedIn = () => {
fetch('/user-status')
.then((res) => res.json())
.then((data) => this.setState({ loggedin: data }));
};
render() {
if (this.state.loggedin == 'true') {
return (
<a className='nav-item nav-link' href='/logout'>
Logout
</a>
);
}
return (
<nav className='navbar navbar-expand-lg navbar-dark bg-primary'>
<a className='navbar-brand' href='#'>
Navbar
</a>
<button
className='navbar-toggler'
type='button'
data-toggle='collapse'
data-target='#navbarNavAltMarkup'
aria-controls='navbarNavAltMarkup'
aria-expanded='false'
aria-label='Toggle navigation'
>
<span className='navbar-toggler-icon'></span>
</button>
<div className='collapse navbar-collapse' id='navbarNavAltMarkup'>
<div className='navbar-nav'>
<a className='nav-item nav-link active' href='#'>
Home <span className='sr-only'>(current)</span>
</a>
<a className='nav-item nav-link' href='/about'>
About
</a>
<a className='nav-item nav-link' href='/signin'>
Sign in/Sign Up
</a>
<a className='nav-item nav-link' href='/profile'>
My Profile
</a>
<a className='nav-item nav-link' href='/leaderboard'>
Top Forecasts
</a>
</div>
</div>
</nav>
);
}
}
export default Navbar;

Data not refreshing after login to homepage in reactjs

I'm saving userdata to localStorage in login component and then redirecting to the homepage. In homepage username is not updating on first visit. I have to reload the page. Then data binds to page after refresh. Please help how can I show data on first visit?
below is my homepage code
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
export default class Header extends Component {
constructor(props) {
super(props);
this.state = {
isLogin: false,
isLogout: false,
user: ""
};
}
componentDidMount() {
const userData = localStorage.getItem("userData");
const user = JSON.parse(userData);
this.setState({ user: user });
if (userData) {
this.setState({ isLogin: true });
}
console.log(userData);
console.log(user);
}
logout = e => {
e.preventDefault();
localStorage.clear();
this.setState({ isLogout: true });
};
render() {
if (this.state.isLogin === false || this.state.isLogout === true) {
return (
<header
id="kr-header"
className="kr-header cd-auto-hide-header kr-haslayout"
>
<div className="container">
<div className="row">
<div className="col-xs-12 col-sm-12 col-md-12 col-lg-12">
<strong className="kr-logo">
<Link to="/">
<img src="images/logo.png" alt="company logo here" />
</Link>
</strong>
<nav className="kr-addnav">
<ul>
<li>
<Link
id="kr-btnsignin"
className="kr-btn kr-btnblue"
to="login_register"
>
<i className="icon-smiling-face" />
<span>Join Now</span>
</Link>
</li>
<li>
<a
className="kr-btn kr-btngreen"
href="dashboardaddlisting.html"
>
<i className="icon-plus" />
<span>Add Listing</span>
</a>
</li>
</ul>
</nav>
<nav id="kr-nav" className="kr-nav">
<div className="navbar-header">
<button
type="button"
className="navbar-toggle collapsed"
data-toggle="collapse"
data-target="#kr-navigation"
aria-expanded="false"
>
<span className="sr-only">Toggle navigation</span>
<span className="icon-bar" />
<span className="icon-bar" />
<span className="icon-bar" />
</button>
</div>
<div
id="kr-navigation"
className="collapse navbar-collapse kr-navigation"
>
<ul>
<li>
Dasboard
</li>
</ul>
</div>
</nav>
</div>
</div>
</div>
</header>
);
} else {
return (
<header
id="kr-header"
className="kr-header cd-auto-hide-header kr-haslayout"
>
<div className="container">
<div className="row">
<div className="col-xs-12 col-sm-12 col-md-12 col-lg-12">
<strong className="kr-logo">
<Link to="/">
<img src="images/logo.png" alt="company logo here" />
</Link>
</strong>
<nav className="kr-addnav">
<ul>
<li>
<Link
id="kr-btnsignin"
className="kr-btn kr-btnblue"
to="login_register"
>
<i className="icon-smiling-face" />
<span>{this.state.user.user.firstname}</span>
</Link>
</li>
<li>
<a
className="kr-btn kr-btngreen"
href="dashboardaddlisting.html"
>
<i className="icon-plus" />
<span>Add Listing</span>
</a>
</li>
<li>
<a onClick={this.logout} className="kr-btn kr-btngreen">
<i className="icon-plus" />
<span>Logout</span>
</a>
</li>
</ul>
</nav>
<nav id="kr-nav" className="kr-nav">
<div className="navbar-header">
<button
type="button"
className="navbar-toggle collapsed"
data-toggle="collapse"
data-target="#kr-navigation"
aria-expanded="false"
>
<span className="sr-only">Toggle navigation</span>
<span className="icon-bar" />
<span className="icon-bar" />
<span className="icon-bar" />
</button>
</div>
<div
id="kr-navigation"
className="collapse navbar-collapse kr-navigation"
>
<ul>
<li>
Dasboard
</li>
</ul>
</div>
</nav>
</div>
</div>
</div>
</header>
);
}
}
}
Below is login-register component code
import React, {Component} from 'react';
import { Link,Redirect ,withRouter } from 'react-router-dom';
import PropTypes from "prop-types";
import Otp from './otp';
import axios from '../api';
export default class LoginRegister extends Component {
static contextTypes = {
router: PropTypes.object
}
constructor(props,context){
super(props,context);
this.state = {
fname:'',
lname:'',
emailaddress:'',
password:'',
mobile:'',
user:'',
login_pass:'',
isLogin:false
}
this.regi_data = this.regi_data.bind(this);
this.login_data = this.login_data.bind(this);
// this.otpModalRef = React.createRef();
}
regi_data(e){
this.setState({[e.target.name] : e.target.value}
);
}
login_data(e){
this.setState({[e.target.name] : e.target.value})
}
// otpModalRef = ({onOpenModal}) => {
// this.showModal = onOpenModal;
// }
componentDidMount(){
if (localStorage.getItem('userData')) {
this.context.router.history.push({
pathname:'/',
});
}
}
login = (e) => {
e.preventDefault();
axios.post('/api/signin', {
user:this.state.user,
password:this.state.login_pass,
})
.then(res => {
//console.log(res);
localStorage.setItem('userData', JSON.stringify(res.data));
this.context.router.history.push({
pathname:'/',
});
// window.location.reload();
this.setState({isLogin: true});
})
.catch(function (error) {
console.log(error.message);
})
}
register = (e) => {
e.preventDefault();
axios.post('/api/user/add', {
firstname: this.state.fname,
lastname:this.state.lname,
email:this.state.emailaddress,
password:this.state.password,
mobile:this.state.mobile
},
)
.then(res => {
console.log(res);
// this.showModal();
this.context.router.history.push({
pathname:'/otp_validate',
});
}).catch(function(error){
alert(error.message)
})
}
ISSUE
From the code you have shown above and the issue you are facing, it looks like you have a common component Header that is rendered from the parent component of Login and HomePage, probably from the central App component where you must have also declared the routes for Login and Homepage. If this is the case, the issue you are facing is that when the App loads for the first time, the Header also loads at that time and its componentDidMount method gets called. But since you are not logged in at this time, the header component does not get the user data needed to show the username. Later whenever you perform the actual log in action, store the data in localstorage and redirect to homepage, the header does not get unmounted and remounted because it is outside the scope of these individual Login and Homepage components, so it's componentDidMount event will not be triggered and there will not be any change detected in the header component.
FIX
Approach 1: Either create two different Header Components, one for logged in state and one for logged out state and place them inside the render methods of Login and HomePage components respectively. In this case, the above localstorage logic written in componentDidMount of these Header components shall work properly.
Approach 2: Lift up the user data to the parent of Header component and pass the user data as a prop to this component. In this case you can directly use the property in your Header's render method.
try like this in login component
login = (e) => {
e.preventDefault();
axios.post('/api/signin', {
user:this.state.user,
password:this.state.login_pass,
})
.then(res => {
localStorage.setItem('userData', JSON.stringify(res.data));
this.context.router.history.push({
pathname:'/',
state: { userData: JSON.stringify(res.data) }
});
this.setState({isLogin: true});
})
.catch(function (error) {
console.log(error.message);
})
}
and in homepage check props in componentDidMount
componentDidMount() {
const { userData } = this.props.location.state
// const user = JSON.parse(userData);
this.setState({ user: userData });
if (userData) {
this.setState({ isLogin: true });
}
console.log(userData);
console.log(user);
}
Here you are passing props to home page after login. It should work properly. If not please ask
Write these two lines at top of render() method. Like this:
render() {
const userData = localStorage.getItem("userData");
const user = JSON.parse(userData);
if (user) {
return (...); // logged in ui
} else {
return (...); // logged out ui
}
}
componentDidMount() {
const userData = localStorage.getItem("userData");
const user = JSON.parse(userData);
this.setState({ user: user });
if (userData) {
this.setState({ isLogin: true });
}
console.log(userData);
console.log(user);
this.setState({})
}
Try this approach.
login = (e) => {
e.preventDefault();
axios.post('/api/signin', {
user:this.state.user,
password:this.state.login_pass,
})
.then(res => {
localStorage.setItem('userData', JSON.stringify(res.data));
// delay the redirection after udpated the local storage.
setTimeout(() => {
this.context.router.history.push({
pathname:'/',
});
this.setState({isLogin: true});
}, 500);
})
.catch(function (error) {
console.log(error.message);
})
}
In your header component, you are deriving the data to display from two sources of truth. LocalStorage and Components state.
This will cause problems because now you have to make sure that the two sources are in sync, which is the problem you are facing currently.
If I look at your Header component, you are deriving the state from LocalStorage, so if we can get rid of the use of state react would always try to render your header component and you would avoid your problem of trying to keep the two sources of data in sync.
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
const Header = (props) => {
let userData = localStorage.getItem("userData");
if (userData) { // i.e. user IS logged in
let user = JSON.parse(userData);
return ( /* Your code for showing user data. in logout link onClick, clear the local storage */ )
} else {
return ( /*Your login/register header*/)
}
}
export default Header;
If you are worried about the performance, first measure the impact. If your userData is not a deeply nested huge json, odds are performance overhead will be negligible. Remember, React calling the render method does not mean it will paint the dom.
One assumption I am making: You can rely on LocalStorage as the single source of truth. Ideally I would advice on having some cache invalidation logic in there, but it really depends on your usecase and other security measures you have in place.
I believe it's running so fast that when you redirect the user to the homepage, after the login, the userData was not written to localStorage yet.
So you need to check first if the data was written before the redirect.
const asyncLocalStorage = {
setItem: async function (key, value) {
await null
return localStorage.setItem(key, value)
},
getItem: async function (key) {
await null
return localStorage.getItem(key)
}
}
asyncLocalStorage.setItem('user', 'data')
.then( () => asyncLocalStorage.getItem('user') )
.then( data => {
console.log('User', data)
// Redirect ...
} )
Dude your problem is that you have 3 flags that pretty much do the same and you're handling them the wrong way.
for example, this line
if (this.state.isLogin === false || this.state.isLogout === true)
will is wrong from the get-go, you initialize both flags as false, so you'll go straight to the else condition.
look at this other line right here
if (userData) {
this.setState({ isLogin: true });
}
this code never resets the isLogout flag, and the logout method also has issues
logout = e => {
e.preventDefault();
localStorage.clear();
this.setState({ isLogout: true });
};
if you login then isLogin becomes true and isLogout stays false.
if you logout then isLogout becomes true and isLogin stays true!
at the end of the day, if you don't have userdata, aka your user is null, then you're logged out, no matter how many booleans say the opposite, you have a redundancy of logic issue and you need to simplify your app.
setState on componentWillMount
everything is same as in your componentDidMount, but place it inside of the componentWillMount
If you are using Header component independent of login and home component, they you should use getDerivedStateFromProps(props) instead of componentDidMount, as componentDidMount is called only after initial render.
You can use getDerivedStateFromProps(props, state) life cycle component as it executes before the initial render and also for each re-rendering. The componentDidMount() life cycle method is called after the render method got executed, that too only after the initial render. So setting the state here will be reflected after the component got re-rendered. But the getDerivedStateFromProps is called before the render method is called. You may check the condition there, if there are no changes just return null otherwise update the state there. In getDerived state from props, you may set the state by returning an object. the setState function won't work here, since it is a static method. kindly refer this link https://reactjs.org/docs/react-component.html#static-getderivedstatefromprops
Use the code as like below
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
export default class Header extends Component {
constructor(props) {
super(props);
this.state = {
isLogin: false,
isLogout: false,
user: {}
};
}
static getDerivedStateFromProps(props, state){
const userData = localStorage.getItem("userData");
const user = JSON.parse(userData);
if (state.user !== userData){
return {
user: user,
isLogin: true
}
}
return null;
}
logout = e => {
e.preventDefault();
localStorage.clear();
this.setState({ isLogout: true, isLogin: false });
};
render() {
if (this.state.isLogin === false || this.state.isLogout === true) {
return (
<header
id="kr-header"
className="kr-header cd-auto-hide-header kr-haslayout"
>
<div className="container">
<div className="row">
<div className="col-xs-12 col-sm-12 col-md-12 col-lg-12">
<strong className="kr-logo">
<Link to="/">
<img src="images/logo.png" alt="company logo here" />
</Link>
</strong>
<nav className="kr-addnav">
<ul>
<li>
<Link
id="kr-btnsignin"
className="kr-btn kr-btnblue"
to="login_register"
>
<i className="icon-smiling-face" />
<span>Join Now</span>
</Link>
</li>
<li>
<a
className="kr-btn kr-btngreen"
href="dashboardaddlisting.html"
>
<i className="icon-plus" />
<span>Add Listing</span>
</a>
</li>
</ul>
</nav>
<nav id="kr-nav" className="kr-nav">
<div className="navbar-header">
<button
type="button"
className="navbar-toggle collapsed"
data-toggle="collapse"
data-target="#kr-navigation"
aria-expanded="false"
>
<span className="sr-only">Toggle navigation</span>
<span className="icon-bar" />
<span className="icon-bar" />
<span className="icon-bar" />
</button>
</div>
<div
id="kr-navigation"
className="collapse navbar-collapse kr-navigation"
>
<ul>
<li>
Dasboard
</li>
</ul>
</div>
</nav>
</div>
</div>
</div>
</header>
);
} else {
return (
<header
id="kr-header"
className="kr-header cd-auto-hide-header kr-haslayout"
>
<div className="container">
<div className="row">
<div className="col-xs-12 col-sm-12 col-md-12 col-lg-12">
<strong className="kr-logo">
<Link to="/">
<img src="images/logo.png" alt="company logo here" />
</Link>
</strong>
<nav className="kr-addnav">
<ul>
<li>
<Link
id="kr-btnsignin"
className="kr-btn kr-btnblue"
to="login_register"
>
<i className="icon-smiling-face" />
<span>{Object.entries(this.state.user).length > 0 ? this.state.user.user.firstname : `-`}</span>
</Link>
</li>
<li>
<a
className="kr-btn kr-btngreen"
href="dashboardaddlisting.html"
>
<i className="icon-plus" />
<span>Add Listing</span>
</a>
</li>
<li>
<a onClick={this.logout} className="kr-btn kr-btngreen">
<i className="icon-plus" />
<span>Logout</span>
</a>
</li>
</ul>
</nav>
<nav id="kr-nav" className="kr-nav">
<div className="navbar-header">
<button
type="button"
className="navbar-toggle collapsed"
data-toggle="collapse"
data-target="#kr-navigation"
aria-expanded="false"
>
<span className="sr-only">Toggle navigation</span>
<span className="icon-bar" />
<span className="icon-bar" />
<span className="icon-bar" />
</button>
</div>
<div
id="kr-navigation"
className="collapse navbar-collapse kr-navigation"
>
<ul>
<li>
Dasboard
</li>
</ul>
</div>
</nav>
</div>
</div>
</div>
</header>
);
}
}
}

React - How to change the props of a child component when an event happens?

I have a component called Nav. I want send props to another component called Body when the user use types in a search bar. The issue I'm having is that the value in the Body component does not change when I type in the search bar.
The Nav class:
constructor(props) {
super(props);
this.state = { id: "" };
this.handleChange = this.handleChange.bind(this);
}
handleChange = event => {
this.setState({ id: event.target.value });
};
render() {
return (
<div>
<nav className="navbar navbar-expand-lg navbar-dark bg-dark">
<button
className="navbar-toggler"
type="button"
data-toggle="collapse"
data-target="#navbarTogglerDemo03"
aria-controls="navbarTogglerDemo03"
aria-expanded="false"
aria-label="Toggle navigation"
>
<span className="navbar-toggler-icon" />
</button>
<a className="navbar-brand" href="#">
<img src="https://icon-icons.com/icons2/1412/PNG/48/comics-spiderman-cam_97492.png" />
Home
</a>
<div className="collapse navbar-collapse" id="navbarTogglerDemo03">
<ul className="navbar-nav mr-auto mt-2 mt-lg-0">
<li className="nav-item active">
<a className="nav-link" href="#">
<span class="sr-only">(current)</span>
</a>
</li>
<li className="nav-item">
<a className="nav-link" href="#">
Link
</a>
</li>
<li className="nav-item">
<a
className="nav-link disabled"
href="#"
tabindex="-1"
aria-disabled="true"
>
Disabled
</a>
</li>
</ul>
<form
className="form-inline my-2 my-lg-0"
onSubmit={() => {
console.log(this.state.id);
}}
>
<input
className="form-control mr-sm-2"
type="search"
placeholder="Search"
aria-label="Search"
value={this.state.id}
onChange={this.handleChange}
/>
<button className="btn btn-light" type="submit">
Search
</button>
</form>
</div>
</nav>
<Body valorId={this.state.id} />
{alert(this.state.id)}
</div>
);
}
The body class:
constructor(props) {
super(props);
this.state = { heroe: null, loading: true };
}
async componentDidMount() {
const url = "https://superheroapi.com/api/2132321/" + this.props.valorId;
console.log("aca va:" + this.props.valorId);
alert(url);
const response = await fetch(url);
const data = await response.json();
console.log(data.name);
this.setState({ heroe: data, loading: false });
}
Thank you.
componentDidMount will only run when the component is first mounted. You could instead move the logic to a separate method and call that both in componentDidMount and in componentDidUpdate when the valorId prop changes.
Example
class Body extends React.Component {
constructor(props) {
super(props);
this.state = { heroe: null, loading: true };
}
componentDidMount() {
this.loadHeroe();
}
componentDidUpdate(prevProps) {
if (this.props.valorId !== prevProps.valorId) {
this.loadHeroe();
}
}
loadHeroe = async () => {
this.setState({ loading: true });
const url = "https://superheroapi.com/api/2132321/" + this.props.valorId;
const response = await fetch(url);
const data = await response.json();
this.setState({ heroe: data, loading: false });
};
render() {
// ...
}
}
The problem is that you use componentDidMount
This function is called one time, at the mount of the component.
Try to use componentDidUpdate instead

ReactJs setState undefined when using react-autoBind

Getting this Uncaught TypeError: Cannot read property 'setState' of undefined when trying to setState in a function. I'm aware that there are other similar issues already posted but none of them are using react-autobind
this works when I bind the function in the constructor Like so :
this.toggleNav = this.toggleNav.bind(this)
However I would like to accomplish this with react-autobind
import React from "react";
import ReactDOM from "react-dom";
import ScrollspyNav from "react-scrollspy-nav";
import autoBind from 'react-autobind';
export default class Navigation extends React.Component {
constructor(props) {
super(props);
autoBind(this);
this.state = {
toggleMobileNav: false
};
}
toggleNav() {
this.setState((prev, props) => {
return {
toggleMobileNav: !prev.toggleMobileNav
}
});
}
render() {
return(
<ScrollspyNav
scrollTargetIds={["page-top", "about", "projects", "signup"]}
activeNavClass="is-active"
scrollDuration="1000"
headerBackground="false"
activeNavClass="active-nav"
>
<nav className="navbar navbar-expand-lg navbar-light fixed-top" id="mainNav">
<div className="container">
<a id="page-top" className="navbar-brand js-scroll-trigger" href="#page-top">Start Bootstrap</a>
<button className="navbar-toggler navbar-toggler-right" onClick={this.toggleNav} type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
Menu
<i className="fas fa-bars"></i>
</button>
<div className= {this.state.toggleMobileNav ? "show collapse navbar-collapse" : " " + 'collapse navbar-collapse'} id="navbarResponsive">
<ul className="navbar-nav ml-auto">
<li className="nav-item">
<a className="nav-link" href="#about">About</a>
</li>
<li className="nav-item">
<a className="nav-link" href="#projects">Projects</a>
</li>
<li className="nav-item">
<a className="nav-link" href="#signup">Contact</a>
</li>
</ul>
</div>
</div>
</nav>
</ScrollspyNav>
)
}
}
If you use arrow function syntax to declare your method, you will not need to call this.toggleNav = this.toggleNav.bind(this) or use react-autobind
toggleNav = () => {
this.setState((prev, props) => {
return {
toggleMobileNav: !prev.toggleMobileNav
}
});
}
Here's a Medium post that covers all the options: https://medium.com/komenco/react-autobinding-2261a1092849

How to import dropdown with autocomplete in the navbar

Im really new in the react and may be my code is very wrong, but how can create a dropdown with autocomplete in the navbar with bootstrap in React JS.
I read too much examples but my code never run..
I try to create dropdown with create-select but I dont know exactly how..
My code:
import React, { Component } from 'react';
import { NavLink } from 'react-router-dom';
import Select from 'react-select';
import 'react-select/dist/react-select.css';
import PropTypes from 'prop-types';
import createClass from 'create-react-class';
export default class Header extends Component {
var ValuesAsBooleansField = createClass({
displayName: 'ValuesAsBooleansField',
propTypes: {
label: PropTypes.string
},
getInitialState () {
return {
options: [
{ value: true, label: 'Yes' },
{ value: false, label: 'No' }
],
value: null
};
},
onChange(value) {
this.setState({ value });
console.log('Boolean Select value changed to', value);
},
render () {
return (
<div className="section">
<h3 className="section-heading">{this.props.label} (Source)</h3>
<Select
onChange={this.onChange}
options={this.state.options}
simpleValue
value={this.state.value}
/>
<div className="hint">This example uses simple boolean values</div>
</div>
);
}
});
module.exports = ValuesAsBooleansField;
render() {
return (
<nav class="navbar navbar-toggleable-md navbar-inverse bg-inverse ">
<button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<a class="navbar-brand" href="#">WeatherGraph</a>
<div class="collapse navbar-collapse" id="navbarNavDropdown">
<ul class="navbar-nav">
<li class="nav-item active">
<a class="nav-link" href="#">Начало <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Времето</a>
</li>
</ul>
</div>
</nav>
);
}
}
You have mixed up the Es5 and Es6 coding style. Just update your code and it's working fine now.
import React, { Component } from 'react';
import Select from 'react-select';
import 'react-select/dist/react-select.css';
export default class ValuesAsBooleansField extends Component {
constructor(props){
super(props);
this.state = {
options: [
{ value: true, label: 'Yes' },
{ value: false, label: 'No' }
],
value: null
}
this.onChange = this.onChange.bind(this);
}
onChange(event) {
this.setState({value: event.value});
console.log('Boolean Select value changed to', event.value);
}
render () {
return (
<div className="section">
<h3 className="section-heading">{this.props.label} (Source)</h3>
<Select
onChange={this.onChange}
options={this.state.options}
value={this.state.value}
/>
<div className="hint">This example uses simple boolean values</div>
</div>
);
}
}
**************** Paste this code in Header.js file *************
import React, { Component } from 'react';
import ValuesAsBooleansField from './ValuesAsBooleansField';
export default class Header extends Component {
render() {
return (
<nav class="navbar navbar-toggleable-md navbar-inverse bg-inverse ">
<button class="navbar-toggler navbar-toggler-right"
type="button" data-toggle="collapse"
data-target="#navbarNavDropdown" aria-controls="navbarNavDropdown"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<ValuesAsBooleansField />
<a class="navbar-brand" >WeatherGraph</a>
<div class="collapse navbar-collapse" id="navbarNavDropdown">
<ul class="navbar-nav">
<li class="nav-item active">
<a class="nav-link">Начало <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item">
<a class="nav-link">Времето</a>
</li>
</ul>
</div>
</nav>
);
}
}

Categories

Resources