show component depending on state - reactjs

I have 2 components that I want to display based on the state.
basically when I click the button I want to toggle 2 components. I dont understand why when I click the button nothing changes.
import React, { Component } from 'react';
import { Route, Link, NavLink, Redirect } from 'react-router-dom'
class Header extends Component {
constructor(props) {
super(props);
this.state = {isToggleOn: false};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return(
<header>
{this.state.isToggleOn ? <MenuOpened /> : <MenuClosed />}
</header>
)
}
}
const MenuClosed = () => {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
)
}
const MenuOpened = () => {
return(
<ul>
<li><NavLink to="/page-1" exact>page1</NavLink></li>
<li><NavLink to="/page-2" exact>page2</NavLink></li>
</ul>
)
}
export default Header;

You gotta pass handleClick function and isToggleOn state as props to MenuClosed component.
{this.state.isToggleOn ? <MenuOpened /> : <MenuClosed handleClick={this.handleClick} isToggleOn={this.state.isToggleOn} />}
And then:
const MenuClosed = ({handleClick, isToggleOn}) => { return ( <button onClick={handleClick}> {isToggleOn ? 'ON' : 'OFF'} </button> ) }

Related

React Re-Render Component on props Change

I have a Tabbar in my Tabbar Component, Which I Change the index props in it :
class Tabbar extends Component {
state = {
index: this.props.index,
name: this.props.name,
image: this.props.image
};
changeTabs = () => {
this.setState({index: this.props.index});
}
render() {
return (
<React.Fragment>
<div id={this.state.index} className="col">
<button onClick={this.changeTabs}></button>
</div>
</React.Fragment>
);
}
}
export default Tabbar;
And Then In my Other Component, I Wanna Re-Render a fragment after props change. Here's my Code :
import Tabbar from './Tabbar';
class Tabview extends Component {
constructor(props) {
super(props);
this.state = {
tabs: [
{index: 0, name: "tab0", image:require('../Assets/profile.svg'),childView: {ProfilePage} },
{index: 1, name: "tab1", image:require('../Assets/home.svg'),childView: {HomePage}},
{index: 2, name: "tab2", image:require('../Assets/blog.svg'),childView: {BlogPage}},
],
}
}
handleRender = () => {
this.state.tabs.map(item => {
if (item.index === this.props.index) {
return <item.childView/>;
}
})
return <BlogPage/>;
}
render() {
return (
<div>
<Header/>
{this.handleRender()}
{this.state.tabs.map(item =>
<Tabbar key={item.index} index={item.index} name={item.name} image={item.image}/>
)}
</div>
);
}
}
export default Tabview;
The Method "handleRender" should handle the rendering.
I tried to use "componentDidMount" or "componentDidUpdate", But I didn't work.
How Can I Make it Work?
Thank you in advance!
You dont need to have a state in the child component for this reason
You can simply have a callback in parent and call it in child component like below.
import React, { Component } from "react";
class Tabbar extends Component {
render() {
return (
<React.Fragment>
<div id={this.props.index} className="col">
<button
onClick={() => this.props.changeTabs(this.props.index)}
></button>
</div>
</React.Fragment>
);
}
}
export default Tabbar;
And in parent you maintain the active index state
import Tabbar from "./Tabbar";
import React, { Component } from "react";
class Tabview extends Component {
constructor(props) {
super(props);
this.state = {
tabs: [
//your tabs
],
activeIndex: 0
};
}
handleRender = () => {
this.state.tabs.map((item) => {
if (item.index === this.state.activeIndex) {
return <item.childView />;
}
});
return <div />;
};
render() {
return (
<div>
{this.handleRender()}
{this.state.tabs.map((item) => (
<Tabbar
key={item.index}
index={item.index}
name={item.name}
image={item.image}
changeTabs={(index) => this.setState({ activeIndex: index })}
/>
))}
</div>
);
}
}
export default Tabview;

react-modal won't close after onClick event from another component

first time exploring Gatsby and tried to install react-modal. Added modal component inside my ComponentOne and inside the modal is another component which is ComponentTwo where the close trigger resides. Already passed the onClick to ComponentTwo but still no luck. I think I miss something.
ComponentOne
import React, { Component } from "react"
import ComponentTwo from "./ComponentTwo"
import Modal from 'react-modal'
export default class ComponentOne extends Component {
constructor(props) {
super(props);
this.state = {
showModal: false
};
this.handleOpenModal = this.handleOpenModal.bind(this);
this.handleCloseModal = this.handleCloseModal.bind(this);
}
handleOpenModal() {
this.setState({ showModal: true });
}
handleCloseModal() {
this.setState({ showModal: false });
}
render() {
return (
<div className="container mx-auto h-50">
<Modal
isOpen={this.state.showModal}
ariaHideApp={false}
style={cotentStyle}
>
<ComponentTwo onClick={this.handleCloseModal} /> // this is component two
</Modal>
</div>
)
}
}
ComponentTwo
import React from 'react'
import { TiDelete } from 'react-icons/ti'
const NavContent = ({ data }, props) => (
<>
<div className="bg-black z-10 absolute inset-0">
<button onClick={props.onClick} className="text-white absolute top-0 right-0 my-4 mx-4">
<TiDelete size={'2em'} />
</button>
</div>
</>
)
export default function Nav(props) {
return (
<StaticQuery
query={worksQuery}
render={data => <NavContent data={data} {...props} />}
/>
)
}
Try with:
export default function Nav({onClick}) {
return (
<StaticQuery
onClick={onClick}
query={worksQuery}
render={data => <NavContent data={data} {...props} />}
/>
)
}

Handling React onToggle event

I am working on the below snippet, and I'd like to find out why I am not able to bind onToggle event with button component. I am getting 'onToggle' is not defined on compiling.
In the main container (Content) I have:
class Content extends Component {
constructor() {
super();
this.state = {
user: dataService.User
}
}
onTogglePane(){
var node = ReactDOM.findDOMNode(this.refs.wrapper);
node.classList.toggle('toggled');
}
onSignOut() {
dataService.Logout((result) => {
this.setState({
user: null
})
});
}
render() {
return (
<div>
<Header
onClick = {this.onSignOut}
onToggle ={this.onTogglePane}
/>
</div>
)
}
}
In the Button.js I have button component as:
import React from 'react';
const Button = ({ text, styleClass, onClick }) => {
return (
<button
type="button"
onClick={e => onClick(e)}
onToggle={e => onToggle(e)}
className={`btn ${styleClass}`}
>
{text}
</button>
);
};
export default Button;
and finally in the Header.js I have
import React from 'react';
import Button from 'components/Button';
class Header extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<nav className="navbar navbar-default hk-navbar fixed-top">
<p className="navbar-brand tk-brand">App</p>
<Button
text={[<i class="icon icon-logout"></i>, " Sign Out"]}
onClick = {(e) => this.props.onClick(e)}
styleClass = 'btn-control'
/>
<Button
text={[<i class="icon icon-logout"></i>, " Full Screen"]}
onToggle = {(e) => this.props.onToggle(e)}
styleClass = 'btn-control'
/>
</nav>
);
}
}
export default Header;
Now I am getting this error:
Failed to compile
./src/components/Button.js
Line 8: 'onToggle' is not defined no-undef
Search for the keywords to learn more about each error.
Try this, you are missing onToggle which you are passing it to Button component.
Below Button component code would fix the issue
import React from 'react';
const Button = ({ text, styleClass, onClick, onToggle }) => {
return (
<button
type="button"
onClick={onClick ? onClick: null}
onToggle={onToggle ? onToggle: null}
className={`btn ${styleClass}`}
>
{text}
</button>
);
};
export default Button;

React - show menu when clicking on just one item from iterating item

I'm having problem with sliding menu in just one item.
When I click on the config button every item shows menu. I tried to figure out something by passing props {mail.id} but I'm afraid I don't understand this.
I would like to have sliding menu just in one item -- the clicked one.
This is ConfigButton
import React, { Component } from "react";
import './Menu.css';
class ConfigButton extends Component {
render() {
return (
<button className="configButton"
onClick={this.props.onClick}
>
<i className="configButtonIcon fas fa-cog"></i>
</button>
);
}
}
export default ConfigButton;
And this is the Component which renders:
import React, { Component } from 'react';
import { NavLink, HashRouter } from 'react-router-dom';
import axios from 'axios';
import Menu from './Menu';
import ConfigButton from './ConfigButton';
const API = myAPI;
const navLinkStyle = {
textDecoration: 'none',
color: '#123e57'
};
class Emails extends Component {
constructor(props) {
super(props);
this.state = {
visible: false,
mails: []
};
this.handleMouseDown = this.handleMouseDown.bind(this);
this.toggleMenu = this.toggleMenu.bind(this);
}
handleMouseDown(e) {
this.toggleMenu();
e.stopPropagation();
}
toggleMenu() {
this.setState({
visible: !this.state.visible
});
}
componentDidMount() {
axios.get(API)
.then(response => {
const mails = response.data;
this.setState({ mails });
})
}
truncate = (text, chars = 140) =>
text.length < chars ? text : (text.slice(0, chars) + '...')
render() {
let mails = this.state.mails;
console.log(mails);
mails = mails.map(mail => {
return (
<div key={mail.id}>
<div className='mail'>
{
!mails.displayed
? <i className="notDisplayed fas fa-circle"></i>
: <i className="displayed far fa-circle"></i>
}
<HashRouter>
<NavLink
to={`/openemail/${mail.id}`}
style={navLinkStyle}
>
<ul className='ulMailWrap'>
<div className='mailHeader'>
<li>{mail.sender}</li>
<li>{mail.created}</li>
</div>
<li>{mail.subject}</li>
<li>{this.truncate(mail.message)}</li>
</ul>
</NavLink>
</HashRouter>
<ConfigButton onClick={this.handleMouseDown} />
<Menu handleMouseDown={this.handleMouseDown}
menuVisibility={this.state.visible}
/>
</div>
</div>
)
});
return (
<div>
{ mails }
</div>
);
}
}
export default Emails;
You can pass a function that will send a different parameter to the handler, depending on value of each element in the array.
Do something like this:
...
<div key={mail.id} onClick={() => this.handleOpenMenu(mail.id)}>
...
Then at the handler:
handleOpenMenu = id => {
// do different stuffs on the id you get here
this.setState({ visibleMenuId: id });
}
And then change the props you are passing to your menu component:
<Menu menuVisibility={this.state.visibleMenuId === mail.id} />

access state of react component from other component

I have the following spinner
import React, { Component } from 'react'
import './Spinner.scss'
export default class Spinner extends Component {
constructor(props) {
super(props);
this.state = {showLoading: true};
}
render () {
return (
<div className="spinner">
<div className="double-bounce1"></div>
<div className="double-bounce2"></div>
</div>
)
}
}
and from other component I would like to show or hide this spinner here is the code of the component:
import React, { Component } from 'react'
import RTable from '../../../components/RTable/RTable'
import Spinner from '../../../components/Spinner/Spinner'
import CsvDownload from '../containers/CsvDownloadContainer'
export default class Table extends Component {
_renderBreadcrumb () {
const { breadcrumb, handleBreadcrumbClick } = this.props
return (
<ol className="breadcrumb">
{(breadcrumb || []).map(el => {
return (
<li key={el.datasetKey}>
<a onClick={() => { handleBreadcrumbClick(el.granularity, el.datasetKey, el.datasetKeyHuman) }}>
{el.datasetKeyHuman}
</a>
</li>
)
})}
</ol>
)
}
render () {
const { datasetRows, columns, metadata, showLoading } = this.props
return (
<div className="row">
<div className="col-sm-12">
{this._renderBreadcrumb()}
<RTable rows={datasetRows} columns={columns} metadata={metadata} />
{ this.props.showLoading ? <Spinner /> : null }
<CsvDownload />
</div>
</div>
)
}
}
as you can see I trying to show or hide the spinner using:
{ this.props.showLoading ? <Spinner /> : null }
but I'm always getting undefinde. Some help please.
You have to move this
constructor(props) {
super(props);
this.state = {showLoading: true};
}
to your <Table /> component, otherwise you access showLoading from <Table />'s props, but it is not passed from anywhere.
Then change also
{ this.props.showLoading ? <Spinner /> : null }
to
{ this.state.showLoading ? <Spinner /> : null }
To show / hide <Spinner /> just call this.setState({ showLoading: Boolean }) in your <Table /> component.

Resources