Clear DOM element after updating state in React - javascript

Hello I am trying to implement an auth logic to my page. The issue I am having, after updating the state of my page, it doesn't remove the authenticated stuffs. How, do I clear the elements?
I am trying to use a ternary operator, it does work. However it doesn't update when the user login or logout. It only renders one time, and it just sits there afterwards.
header.js
import React, { Component } from 'react';
import LoginHeader from './auth_logic/login_header';
import PublicHeader from './auth_logic/public_header';
export default class Header extends Component {
constructor(props) {
super(props);
this.state = {isAuth: false};
}
render() {
if (Meteor.userId) {
this.setState({isAuth: !this.state.isAuth});
}
else {
this.setState({isAuth: !this.state.isAuth});
}
return (
<div>
{this.state.isAuth ? <PublicHeader /> : <LoginHeader />}
</div>
);
}
}
Here is my LoginHeader, when the user is authenticated it will show additional information, and navigation authenticated links.
auth_logic/login_header.js
import React, { Component } from 'react';
import Accounts from '../accounts';
export default class LoginHeader extends Component {
render() {
return (
<div className="nav navbar-default">
<a className="navbar-brand">Nick Reviews</a>
<ul className="nav navbar-nav">
<li>
<a href="#">
To Watch
<span className="badge margin-left-3px">0</span>
</a>
</li>
<li>
<a href="#">
Reviews
<span className="badge margin-left-3px">0</span>
</a>
</li>
<li>
<Accounts />
</li>
<li> Create new item </li>
</ul>
</div>
);
}
}
It is exactly the same as the login_header.js file, but with less information.
auth_logic/public_header.js
import React, { Component } from 'react';
import Accounts from '../accounts';
export default class PublicHeader extends Component {
render() {
return (
<div className="nav navbar-default">
<a className="navbar-brand">Nick Reviews</a>
<ul className="nav navbar-nav">
<li>
<a href="#">
To Watch
<span className="badge margin-left-3px">0</span>
</a>
</li>
<li>
<a href="#">
Reviews
<span className="badge margin-left-3px">0</span>
</a>
</li>
<li>
<Accounts />
</li>
</ul>
</div>
);
}
}

Assuming that your userId is a prop from above, use componentWillReceiveProps() react life cycle method to update your state depends on your props. for more info refer this link - https://reactjs.org/docs/react-component.html

Header component should not keep isAuth state internally, it should come from the container component like Home/About(Router Page)... Change render condition according to prop.

Related

Why does adding useState result in a white screen in my react app?

The code below works completely fine and results in the image below.
import React from "react";
import "./App.css";
import { useState } from "react";
function App(){
return(
<body>
<div className="nav_bar">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"></link>
<ul className="top">
<div className="circle circle1">
<li className="material-icons noselect">
drag_handle
</li>
</div>
<div className="circle circle2">
<li className="material-icons noselect">
home
</li>
</div>
<div className="circle circle3">
<li className="material-icons noselect">
person_outline
</li>
</div>
</ul>
<nav>
<ul className="bottom">
<li className="material-icons noselect" id="feed-bottom">
drag_handle
</li>
<li className="material-icons noselect" id="home-bottom">
home
</li>
<li className="material-icons noselect" id="profile-bottom">
person_outline
</li>
</ul>
</nav>
</div>
</body>
);
}
export default App;
Result
Adding useState to get and set the current state causes the navbar to disappear and show a completely white screen. Specifically I am using useState to change the icon shown in the nav bar to text and to set the currernt state to the icon that is clicked. Code Below
import React from "react";
import "./App.css";
import { useState } from "react";
function App(){
const[selected, setselected] = useState('home');
if(selected === 'feed'){
const feed = document.getElementById('feed-bottom');
feed.innerHTML = 'FEED';
} else if (selected === 'profile') {
const profile = document.getElementById('profile-bottom');
profile.innerHTML = 'PROFILE';
}else{
const home = document.getElementById('home-bottom');
home.innerHTML = 'HOME';
}
return(
<body>
<div className="nav_bar">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"></link>
<ul className="top">
<div className="circle circle1">
<li className="material-icons noselect">
drag_handle
</li>
</div>
<div className="circle circle2">
<li className="material-icons noselect">
home
</li>
</div>
<div className="circle circle3">
<li className="material-icons noselect">
person_outline
</li>
</div>
</ul>
<nav>
<ul className="bottom">
<li className="material-icons noselect" id="feed-bottom" onClick={setselected('profile')}>
drag_handle
</li>
<li className="material-icons noselect" id="home-bottom" onClick={setselected('home')}>
home
</li>
<li className="material-icons noselect" id="profile-bottom" onClick={setselected('profile')}>
person_outline
</li>
</ul>
</nav>
</div>
</body>
);
}
export default App;
I've looked up many post that refernece similar issues but couldn't find one that pretained to mine. I would grealty appreciate some assitance.
This line
home.innerHTML = 'HOME';
will cause an error on mount, because on mount, the React elements returned from App haven't been returned yet - the container is still empty; the #feed-bottom element doesn't exist yet.
While you could fix it by only assigning if the element actually exists, it would be far better to do it the React way and put the values conditionally into the JSX. Don't use vanilla DOM methods in React unless you absolutely have to.
Another problem is that your listeners - eg onClick={setselected('home')} - run when computed (immediately), because you're invoking the setselected function instead of passing a function as a prop to onClick.
You also probably meant to pass feed in the feed-bottom element (instead of profile).
To implement your logic in the React way, you need something like:
<li className="material-icons noselect" id="feed-bottom" onClick={() => setselected('feed')}>
{ selected === 'feed' ? 'FEED' : 'drag_handle' }
</li>
<li className="material-icons noselect" id="home-bottom" onClick={() => setselected('home')}>
{ selected === 'home' ? 'HOME' : 'home' }
home
</li>
<li className="material-icons noselect" id="profile-bottom" onClick={() => setselected('profile')}>
{ selected === 'profile' ? 'PROFILE' : 'person_outline' }
person_outline
</li>
and remove all of the if(selected === 'feed'){ and the like code from the top.

How do I add a class to a react element when it is clicked

I have this react component and I am trying to create a hamburger menu but, I don't know how to add a class when a button is clicked. I am new to React so please go easy on me. When I run the below code it doesn't work and throws an error in the Chrome dev tools.
function Nav() {
const hamburger = document.getElementById('hamburger');
const navUL = document.getElementById('navul');
hamburger.addEventListener('click', () =>{
navUL.classList.toggle('show');
});
return (
<header>
<h2>
<a href="/">
Travis Helms
</a>
</h2>
<nav>
<ul className="flex-row" id='navul'>
<li className="mx-2">
<a href="#about">
About me
</a>
</li>
<li>
<span>Portfolio</span>
</li>
<li>
<span>Contact</span>
</li>
<li>
<span>Resume</span>
</li>
</ul>
</nav>
<button className="hamburger" id="hamburger">
<FontAwesomeIcon icon={faBars}></FontAwesomeIcon>
</button>
</header>
);
}
First of all, welcome to React! It's an amazing framework!
It would have been more helpful if you provided the error you are getting but from the code you shared I can see there are a few things you need to do differently.
Generally, you can use event listeners in React for certain things, and there is a certain way to add them properly, but for the most part they are not used very frequently. Instead, for example, to assign a click event to an element in React, all you need to do is pass it the onClick attribute with the function you want it to execute when it is clicked, for example <button onClick={doSomething}>click me</button>.
An easy way to control the state of your component is by using the useState hook and toggle the class of an element using state. In your case, it could look like this:
import React, { useState } from 'react';
const Nav = () => {
const [isOpen, setIsOpen] = useState(false);
return (
<header>
<h2>
Travis Helms
</h2>
<nav>
<ul className={"flex-row " + (isOpen ? "show" : "")}>
<li className="mx-2">
About me
</li>
<li>
<span>Portfolio</span>
</li>
<li>
<span>Contact</span>
</li>
<li>
<span>Resume</span>
</li>
</ul>
</nav>
<button
className="hamburger"
onClick={() => setIsOpen(!isOpen)}
>
<FontAwesomeIcon icon={faBars}></FontAwesomeIcon>
</button>
</header>
);
};
It looks like you are in a very early stage of learning React and for you to be successful coding with React you need to cover a few basics like state management, props, children and a few more things. I would recommend going on YouTube and find a good introduction video to React and code along to it! There are a lot of great resources in there for people who are just starting with React, just make sure it's something fairly recent (I'd say 2020 onwards).
Ok. So I went to a tutor and worked with them to come up with this solution to toggle my hamburger menu. Just thought I would add this answer for the benefit of other beginning React programmers.
import React,{ useState } from 'react';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import { faBars } from '#fortawesome/free-solid-svg-icons';
function Nav(props) {
const [navClass, setnavClass]= useState("flex-row");
const [toggledNav, settoggledNav]= useState(true);
const addClass=() => {
if (toggledNav){
setnavClass("flex-row show");
settoggledNav(false);
}else {
setnavClass("flex-row");
settoggledNav(true);
}
}
return (
<header>
<h2>
<a href="/">
Travis Helms
</a>
</h2>
<nav>
<ul className={navClass} id='navul'>
<li className="mx-2">
<a href="#about">
About me
</a>
</li>
<li>
<span>Portfolio</span>
</li>
<li>
<span onClick={() => props.setmenuSelect(1)}>Contact</span>
</li>
<li>
<span>Resume</span>
</li>
</ul>
</nav>
<button onClick={addClass} className="hamburger" id="hamburger">
<FontAwesomeIcon icon={faBars}></FontAwesomeIcon>
</button>
</header>
);
}
export default Nav;

ReactJS - valid function not working when called in componentDidMount() and componentDidUpdate()

******** EDITED TO SIMPLIFY EXAMPLE AND CLARIFY REQUIREMENT AND PROBLEM **********
I'm stumped with this one, I hope someone can help.
I have a nav bar that I need to run a function on to add .active classes to li elements if they have descendants of a.active.
The menu system is a React component: -
import React, {Component} from "react";
import { Link, NavLink } from 'react-router-dom'
import {activateMenu} from './ActivateMenu'
class SidebarMenu extends React.Component {
componentDidMount() {
activateMenu()
}
componentDidUpdate() {
activateMenu()
}
render() {
const renderNavLink = (to, text, icon, renderArrow = false) => {
return(
<NavLink to={to}>
<i className="bullet">{icon}</i>
<span>{text}</span>
{renderArrow ? <span className="pull-right-container">
<i className="angle-left"><FaAngleLeft /></i>
</span> : null}
</NavLink>
)
}
return (
<ul className="sidebar-menu" data-widget="tree">
<li className="">
{renderNavLink('/','Home',<FaHome />)}
</li>
<li className="treeview">
{renderNavLink("#",'Users',<FaGroup />, true)}
<ul className="treeview-menu">
<li>
{renderNavLink(userSearchSlug,'Search',<FaSearch />)}
</li>
</ul>
</li>
<button onClick={activateMenu}>Press Me</button>
</ul>
)
}
}
export default SidebarMenu
This will give me an HTML structure like this: -
<ul class="sidebar-menu tree" data-widget="tree">
<li class="treeview">
<a href="#">
<i class="fa fa-dashboard"></i> <span>Links</span>
<span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i>
</span>
</a>
<ul class="treeview-menu">
<li>
<i class="fa fa-circle-o"></i> Link1
</li>
<li>
<i class="fa fa-circle-o"></i> Link2
</li>
</ul>
</li>
</ul>
After React has rendered the HTML, I need to trigger a click event on the the .treeview > a node if any a.active nodes are found under .treeview-menu. So: -
<li class="treeview">
<a href="#" *****TRIGGER CLICK EVENT*****>
<i class="fa fa-dashboard"></i> <span>Links</span>
<span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i>
</span>
</a>
<ul class="treeview-menu">
<li>
<i class="fa fa-circle-o *****.ACTIVE CLASS HERE****"></i> Link1
</li>
<li>
<i class="fa fa-circle-o"></i> Link2
</li>
</ul>
</li>
activeMenu() looks like this: -
$('ul.sidebar-menu li.treeview:not(.menu-open)').has('a.active').find('a').trigger( "click" );
This function works when called from onClick() from a button on the page but it is not working in componentDidMount() and componentDidUpdate(). The function will run (tested with console.log() but not affect the HTML as it should. However, if I run it from a Button, it works perfectly. It also works perfectly when HMR runs.
I've no idea why this is happening. Does anyone have any ideas?
This is probably happening because you're selecting the element directly rather than using refs, although it's hard to say because we have no idea what $('ul.sidebar-menu .treeview a').parent().has('a.active').parent().find('.treeview a') is selecting, which is why this kind of code is an antipattern.
React may be in some state where it's not prepared to handle click events at those points. Try using something like the following:
import React, {Component} from "react";
import { Link, NavLink } from 'react-router-dom'
class SidebarMenu extends React.Component {
constructor(props) {
super(props);
this.menuRefs = [];
}
componentDidUpdate() {
if (this.menuRefs.length) {
this.menuRefs[0].click();
}
}
render() {
const renderNavLink = (to, text, icon, renderArrow = false) => {
return(
<NavLink to={to} innerRef={ref => this.menuRefs.push(ref)}>
<i className="bullet">{icon}</i>
<span>{text}</span>
{renderArrow ? <span className="pull-right-container">
<i className="angle-left"><FaAngleLeft /></i>
</span> : null}
</NavLink>
)
}
return (
<ul className="sidebar-menu" data-widget="tree">
<li className="">
{renderNavLink('/','Home',<FaHome />)}
</li>
<li className="treeview">
{renderNavLink("#",'Users',<FaGroup />, true)}
<ul className="treeview-menu">
<li>
{renderNavLink(userSearchSlug,'Search',<FaSearch />)}
</li>
</ul>
</li>
<button onClick={() => this.menuRefs[0] && this.menuRefs[0].click()}>Press Me</button>
</ul>
)
}
}
export default SidebarMenu
Notice
Now there's an array of "menuRefs" and you just use them like normal DOM elements.
We push to the menuRefs in the NavLink innerRef prop (found here)
Note however that you may want to keep a map to ensure that no duplicates get pushed into menuRefs.
To learn more about refs, visit the docs: https://reactjs.org/docs/refs-and-the-dom.html

How do I reuse a method to handle multiple, different refs in React?

I am using jQuery for some UI manipulation on a React app. (I know, I know... :)
Why do I need to write different methods (which are identical in behavior) in order to toggle separate parts of my navigation bar?
First I tried to create and pass just one method to both click listeners bellow, only the first ref would toggle regardless of clicking on both elements listening for the click event.
Now, I have it working but rather with two diff methods doing the exact same thing.
That seems far from a DRY approach.
Am I missing something?
import React, { Component } from 'react';
import { findDOMNode } from 'react-dom';
import $ from 'jquery';
class Navigation extends Component {
componentDidMount() {
this.$el = $(this.el);
this.$el.slideToggle();
}
componentWillUnmount() {
this.$el.slideToggle('destroy');
}
//handle first dropdown
portifolioDropdownToggle = () => {
const el = findDOMNode(this.refs.ptoggle);
$(el).slideToggle();
}
//handle second dropdown
servicesDropdownToggle = () => {
const el = findDOMNode(this.refs.servtoggle);
$(el).slideToggle();
}
render() {
return (
<div>
<nav>
<ul className="nav-list" >
<li>Home</li>
<li>About</li>
{/* First Dropdown */}
<li>
<a href="#!" onClick={this.servicesDropdownToggle}>Services</a>
<ul className="nav-dropdown" ref="servtoggle">
<li>
Web Design
</li>
<li>
Web Development
</li>
</ul>
</li>
{/* Second Dropdown */}
<li>Pricing</li>
<li>
<a href="#!" onClick={this.portifolioDropdownToggle}>Portfolio</a>
<ul className="nav-dropdown" ref="ptoggle">
<li>
Web Design
</li>
<li>
Web Development
</li>
</ul>
</li>
</ul>
</nav>
</div>
)
}
}
export default Navigation;
If you pass an identifier as an argument to the method then you can reuse it.
handleToggle = (thingToToggle) => {
const el = findDOMNode(thingToToggle);
$(el).slideToggle();
}
Then in your render method, you can use it like so...
<a href="#!" onClick={() => this.handleToggle(this.refs.ptoggle)}>Portfolio</a>
and reuse it like so...
<a href="#!" onClick={() => this.handleToggle(this.refs.servtoggle)}>Services</a>

React application with side menu and display components based on the selected menu

I am pretty new to developing in React and Javascript. I am bulding an application with a side menu. I wanted to display different components based on the selection in side menu. Can someone guide me how to do this. I am attaching my menu component and index code here.
class AppIndex extends Component {
constructor(props) {
super(props);
}
render () {
return (
<Navbar />
<div className="row">
<div className="col-md-2">
<MenuComponent/>
</div>
<div className="col-md-10">
// I need to add the components here based on selected item in menu
</div>
</div>
);
}
}
class MenuComponent extends Component {
constructor(props) {
super(props);
}
render() {
return (
<ul className=" nav nav-pills mr-auto flex-column">
<li className="nav-item">
Overview
</li>
<li className="nav-item">
Component1
</li>
<li className="nav-item">
Component2
</li>
<li className="nav-item">
Component3
</li>
<li className="nav-item">
Component4
</li>
<li className="nav-item">
Component5
</li>
</ul>
);
}
}
im sorry i didnt see your top level component.
Now from my experience, i wanna give you a little advice, don't let any child component render another component. try to avoid that as much as possible.
Here's what i would do.
This is the Top Level Component
class AppIndex extends Component {
constructor(props) {
super(props);
this.state = { component: '' };
this.selectComponent = this.selectComponent.bind(this); // dont forget to bind
}
selectComponent(event){ // this will take the name of the button thats beeing clicked and sets name of button to state
event.preventDefault();
this.setState({component: event.target.name});
}
render () {
let toRender = null;
switch(this.state.component)
{
case "component 1":
toRender = <Component1/>
case "component 2":
toRender = <Component2/>
case "component 3":
toRender = <Component3/>
}
return (
<Navbar />
<div className="row">
<div className="col-md-2">
<MenuComponent onClick = {this.selectComponent}/>
</div>
<div className="col-md-10">
{toRender} //here goes the component
</div>
</div>
);
}
}
this is the menu
const MenuComponent = ({onClick}) => { // you dont need a class component for this
return (
<ul className=" nav nav-pills mr-auto flex-column">
<li className="nav-item">Overview</li>
<li className="nav-item"> <button onClick = {onClick} name = "component 1">Component1</button></li>
<li className="nav-item"> <button onClick = {onClick} name = "component 2">Component2</button></li>
<li className="nav-item"> <button onClick = {onClick} name = "component 3">Component3</button></li>
</ul>
);
}
thats it.
easy.. You already have the name of the button in the top level component's state right?
send it down as props to the MenuComponent and on every button write something like,
here im assuming the name of the props the MenuComponent get is name
<li className="nav-item"> <button className = {this.props.name === "component 1"?"Active":""} onClick = {onClick} name = "component 1">Component1</button></li>

Categories