How can I use useState to conditionally render? - reactjs

I am trying to use useState to conditionally render a div but for the life of me I can not figure out how to do it. I think I am close but I can not see what I am doing wrong, I have entirely misunderstood how to do this. What am I doing wrong? I have written this and it does not work..
import React, { useState } from 'react'
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
import { faWindowClose } from '#fortawesome/free-solid-svg-icons'
function ElementMenuHeader() {
const [elementMenuOpenClose, setElementMenuOpenClose] = useState(true)
const handleClick = () => setElementMenuOpenClose(false)
return (
<div id="App-Close-Element-Menu-Container"
style={{ display: elementMenuOpenClose ? 'block' : 'none'}}
>
<button id="App-Close-Element-Menu"
onClick={() => handleClick }
>
<FontAwesomeIcon icon={faWindowClose} />
</button>
</div>
);
}
export default ElementMenuHeader
Ideally I would like to be able to set the state of elementMenuOpenClose from other components too, but I will cross this bridge first I think.

You can just use a ternary operator and check a against the state in the operator condition return the div you would like based on the state:
{(elementMenuOpenClose? <div>some tags A </div>: <div>Some tags B </div>)}

You may have a parent component of ElementMenuHeader.
Let's say that is ParentElementMenuHeader.
Inside ParentElementMenuHeader component, you can define like
const ParentElementMenuHeader = () => {
const [elementMenuOpenClose, setElementMenuOpenClose] = useState(true);
...
return (
...
{elementMenuOpenClose && (
<ElementMenuHeader
setElementMenuOpenClose={setElementMenuOpenClose}
...
/>
)}
...
);
}
Inside ElementMenuHeader component,
import React, { useState } from 'react'
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
import { faWindowClose } from '#fortawesome/free-solid-svg-icons'
function ElementMenuHeader({setElementMenuOpenClose}) {
const handleClick = () => setElementMenuOpenClose(false)
return (
<div id="App-Close-Element-Menu-Container">
<button id="App-Close-Element-Menu" onClick={handleClick}>
<FontAwesomeIcon icon={faWindowClose} />
</button>
</div>
);
}
export default ElementMenuHeader

Related

Prevent update of all components on button click

I have an array of objects and displayed them using map. A '+' button associated with each component. I want to update the count of a specific component on button click. I tried with the following code. However, it is updating the count of all the other components also. What can I do to fix this?
import { useState } from 'react';
import { people } from './Data.js';
function App() {
const[count,setCount]=useState(0)
const updateCount=()=>{
setCount(count+1)
}
return (
<div>
<h4>List of Items</h4>
{
people.map((item)=>{
return (
<div key={item.id}>count:{count}
<button onClick={()=>updateCount}>+</button>
</div>
)
})
}
</div>
);
}
export default App;
You need to manage the state individually. Here's a possible alternative, hope it helps.
import React,{ useState } from 'react';
import { people } from './Data.js';
function PeopleItem(props) {
const[count,setCount]=useState(0)
const updateCount=()=>setCount(count+1)
const {item} = props
return <div>count:{count}
<button onClick={updateCount}>+</button>
</div>
}
export function App() {
return (
<div>
<h4>List of Items</h4>
{
people.map((item)=> <PeopleItem key={item.id} item={item}/>)
}
</div>
);
}
If you want to update count on a single component you need to initialize the useState inside the child component. Extract the map and create a component for the return statement. Consider the following:
import { useState } from 'react';
import { people } from './Data.js';
function ChildComponent(props){
const[count,setCount]=useState(0)
const updateCount=() => {
setCount(prev => prev + 1)
}
return (<div>
count:{count}
<button onClick={()=>updateCount()}>+</button>
</div>)
}
function App() {
return (
<div>
<h4>List of Items</h4>
{
people.map((item)=>{
return (
<ChildComponent key={item.id}/>
)
})
}
</div>
);
}
export default App;

Is it possible to add an OnClick event in another component?

I added an OnClick event in a custom component created by me, but it didn't work. I want to add an OnClick event to this component. When i click it, it will play a youtube video below. Is it not possible to do this?
Here is my code:
import React, { useState } from "react";
import classes from "./Feature.module.css";
import { ButtonHorizontal, ButtonCircular } from "./Buttons";
function Feature() {
const [open, setOpen] = React.useState(false);
const openVideo = () => {
setOpen((prev) => !prev);
};
return (
<div>
<ButtonHorizontal icon={faPlay} text="Watch Video" onClick={openVideo}/>
{open ? (
<iframe
src="https://www.youtube.com/watch?v=k2qgadSvNyU"
frameborder="0"
allow="autoplay; encrypted-media"
allowfullscreen
title="video"
/>
) : null}
</div>
);
}
export default Feature;
And buttons.js:
import React from "react";
import classes from "./Buttons.module.css";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faPlay} from "#fortawesome/free-solid-svg-icons";
export function ButtonHorizontal(props) {
return (
<div className={classes.buttonHorizontal}>
<FontAwesomeIcon icon={props.icon} className={classes.icon}/>
<div className={classes.text}>{props.text}</div>
</div>
);
}
You need to attach the onClick property to an actual HTML element. Something like <div className={classes.buttonHorizontal} onclick={props.onClick}> should work.
import React from "react";
import classes from "./Buttons.module.css";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import { faPlay} from "#fortawesome/free-solid-svg-icons";
export function ButtonHorizontal(props) {
return (
<div className={classes.buttonHorizontal} onclick={props.onClick}>
<FontAwesomeIcon icon={props.icon} className={classes.icon}/>
<div className={classes.text}>{props.text}</div>
</div>
);
}

How to make a component render conditionally depending on drop down choices on button click. (REACT)

I'm trying to use element variables found in the React documentation here: https://reactjs.org/docs/conditional-rendering.html. I'm not sure why it is not rendering the component I have stored in the Output variable.
Submit:
import './Submit.css'
import React, { useEffect, useState } from "react";
import Output from '../Output/Output'
function Submit(props) {
let output;
const process = () => {
console.log(output)
if(props.src === "x" && props.tgt === "y") {
output = <Output src={props.src} tgt={props.tgt}/>
}
}
return (
<>
<button
disabled={props.src === "Source Chain" || props.tgt === "Target Chain"}
onClick={() => process()} className="submit">Submit
</button>
{output}
</>
);
};
export default Submit;
Output component:
import './Output.css'
import React, { useEffect, useState } from "react";
function Output (props) {
return (
<div className="content">;
<a className="contenttitle">{props.src} to {props.tgt}</a>
<h3 className="contentboxtitle"></h3>
<div className ="liquidassets"> </div>
<h3 className="contentboxtitle"></h3>
<div className ="liquidassets"> </div>
</div>
)
}
export default Output;
Thanks for any help!!
Use a state to control the button and use that state to render or not render your output component
import React, { useState } from "react";
export default function SystemsButtonGroup() {
const [showOutput, setShowOutput] = useState(false);
return (
<div>
<button onClick={() => setShowOutput(!showOutput)} className="submit">
Submit
</button>
{showOutput && <div>Replace this with your output component</div>}
</div>
);
}

ReactJS global state toggle

I am new to reactJS and stuck in an issue. i have a button in header that needs to toggle a class 'show' in a menu which is in some other file. I tried to use global state but do not know how to do that. here is what i did;
LAYOUT FILE
import React, { useState } from "react";
// importing header / menu etc.
function LayoutHome({ children }) {
const [state, setState] = React.useState({ MsgMenu: 'messages-dropdown' });
const handleOpenMsgMenu = (e) => {
e?.preventDefault();
setState({MsgMenu:'messages-dropdown show'});
};
return (
<>
<Header handleOpenMsgMenu={handleOpenMsgMenu} />
<MessageMenu handleOpenMsgMenu={state.MsgMenu} />
{children}
<Footer />
</>
);
}
HEADER
import React, { useState } from "react";
function Header({handleOpenMsgMenu}) {
<button type="button" onClick={handleOpenMsgMenu} className="header-notification-btn">MENU</button >
}
MENU
import React, { useState } from "react";
function MessageMenu({handleOpenMsgMenu}) {
<div id="messages-dropdown" className={handleOpenMsgMenu}>
// CONTENT
</div>
}
To achieve this you can use useState hook to toggle the display of the Menu.
create a new toggle state in global and pass it onto the menu component.
below is the complete code.
import React from "react";
export default function App({children}) {
const [state, setState] = React.useState({ MsgMenu: 'messages-dropdown' });
const [toggle, setToggle] = React.useState(false);
const handleOpenMsgMenu = (e) => {
e?.preventDefault();
setToggle(!toggle);
};
return (
<>
<Header handleOpenMsgMenu={handleOpenMsgMenu} />
<MessageMenu handleOpenMsgMenu={state.MsgMenu} toggle={toggle} />
{children}
</>
);
}
// Header
import React from "react";
function Header({handleOpenMsgMenu}) {
return <button type="button" onClick={handleOpenMsgMenu} className="header-notification-btn">MENU</button >
}
// Menu
import React from "react";
function MessageMenu({handleOpenMsgMenu, toggle}) {
return <div id="messages-dropdown" style={{display: toggle?"block":"none"}}>
<ul>
<li>
{handleOpenMsgMenu}
</li>
</ul>
</div>
}
You can toggle state with !value and then change your class depending on that value
setMenu(() => {
return {
...menu,
show: !menu.show // toggle
};
});
I've made a sample here
For the global state, check out Context or Redux

Why is this infinite loop happening in the following code? React & useState

I'm going to give you 2 versions of a component i wrote. Why does the FIRST one give me an infinite loop, while the second one works fine?
I isolated the problem but i am wondering why the logic doesn't follow 'under the hood'. No doubt some black magic from useState
//THROWS INFINTE LOOP ERROR
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { Popover, PopoverHeader, PopoverBody } from 'reactstrap';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import { faInfoCircle } from '#fortawesome/pro-light-svg-icons';
const HelpIcon = (props) => {
HelpIcon.propTypes = {
title: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
id: PropTypes.string.isRequired,
children: PropTypes.node.isRequired
};
const [isOpen, toggleIsOpen] = useState(false);
return (
<React.Fragment>
<span
className="pointer text-body"
id={props.id}
>
<FontAwesomeIcon icon={faInfoCircle} />
</span>
<Popover
trigger="legacy"
placement="left"
isOpen={isOpen}
target={props.id}
toggle={toggleIsOpen(!isOpen)}{//<-----look here!!!!!!!!!!!!!!!}
>
{props.title !== false && (
<PopoverHeader className="text-body bg-light">
{props.title}
</PopoverHeader>
)}
<PopoverBody className="text-xs cart__rebate_description text-body bg-white">
{props.children}
</PopoverBody>
</Popover>
</React.Fragment>
);
};
export default HelpIcon;
...AND...
//THIS ONE WORKS
//NOTICE THE EXTRA FUNCTION THAT CALLS USESTATE, INSTEAD OF CALLING IT DIRECTLY
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { Popover, PopoverHeader, PopoverBody } from 'reactstrap';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import { faInfoCircle } from '#fortawesome/pro-light-svg-icons';
const HelpIcon = (props) => {
HelpIcon.propTypes = {
title: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
id: PropTypes.string.isRequired,
children: PropTypes.node.isRequired
};
const [isOpen, toggleIsOpen] = useState(false);
const toggle = () => toggleIsOpen(!isOpen);
return (
<React.Fragment>
<span
className="pointer text-body"
id={props.id}
>
<FontAwesomeIcon icon={faInfoCircle} />
</span>
<Popover
trigger="legacy"
placement="left"
isOpen={isOpen}
target={props.id}
toggle={toggle}
>
{props.title !== false && (
<PopoverHeader className="text-body bg-light">
{props.title}
</PopoverHeader>
)}
<PopoverBody className="text-xs cart__rebate_description text-body bg-white">
{props.children}
</PopoverBody>
</Popover>
</React.Fragment>
);
};
export default HelpIcon;
this is wrong, and it causes you the loop:
toggle={toggleIsOpen(!isOpen)} // you call the function in a loop
It should be:
toggle={() => toggleIsOpen(!isOpen)}
If you really want to use it your way you must do a double arrow function like this:
const toggle = isOpen => () => {
toggleIsOpen(isOpen)
}
// and use it like
toggle={toggle(!isOpen)}
You are instantly calling toggleIsOpen in the first example. You should always wrap it with another function if you want to call it with arguments other than toggle prop provides.
it is causing infinite since changing state cause re-render in react; if your function call don't cause re-render it will work( kinda, not causing infinite loop) something like this
foo = () => console.log(9)
toggle={foo()}
but since set state will cause re-render your app stuck; here is the call-stack (kinda)
render is called;
it will reach the set-state
state changes
calling render again
it will reach the set-state
state changes
calling render again
and ....

Resources