why is my if else statement returns true two times? - javascript

so I am new to React and I am trying to learn the basics. I got stuck at a point, where I try to show/hide an element on a page. The problem is that when I click Show details, it works, but Hide details must be clicked 2 times in order to do what its supposed to do.
Can you help me understand this?
import React, { useState } from 'react'
const Playground2 = () => {
let visible = false;
const [details, showDetails] = useState();
const [buttonText, changeButtonText] = useState("Show details");
const toggleVisibility = () => {
if (visible) {
showDetails("");
visible = false;
console.log(visible);
changeButtonText("Show details")
} else {
showDetails("Here are some details");
visible = true;
console.log(visible);
changeButtonText("Hide details");
}
}
return (
<>
<section>
<div className="container">
<h1>Visibility Toggle</h1>
<button onClick={toggleVisibility}>{buttonText}</button>
<p>{details}</p>
</div>
</section>
</>
)
}
export default Playground2

You should use the state in your condition. If you declare a variable like your visible one, this will be assigned on every render (every time you set the state with changeButtonText or showDetails. So every time will be set to false. You can simplify your component by doing:
import React, { useState } from 'react'
const Playground2 = () => {
const [visible, setVisible] = useState();
const toggleVisibility = () => {
setVisible(prevState => !prevState)
}
const buttonText = visible ? 'Hide details' : 'Show details'
const details = 'Here are some details'
return (
<>
<section>
<div className="container">
<h1>Visibility Toggle</h1>
<button onClick={toggleVisibility}>{buttonText}</button>
{visible && <p>{details}</p>}
</div>
</section>
</>
)
}
export default Playground2

Well it'd solve your problem if you turn visibility to state as well.
What I think happening is that when you click on button, the visibility variable is turned to false but component isn't refreshed. In order for component to get refreshed, there must be some change in state.
Maybe try that. That should do the trick.
Tip: Variables like loading, visibility, modal closed/open should be state variables.

Move let visible = false out of the component body and this will work as expected, since you are putting visible inside Component, every time the component updates false will be stored in visible.
let visible = false
const Playground 2 = () => {}

Related

How to render a new popup every time I clicked Grid?

The problem is...
The first popup renders fine.
But when I try to render the second popup, it's not working.
A new popup is not invoked, the previous popup is refreshed.
I want to call a new popup when I clicked a cell in the grid.
my code is like this
const Main = () => {
const [isPopupOpen, setIsPopupOpen] = useState(false);
return (
<>
... other components (including grid)
{ isPopupOpen && <Popup />}
</>
)
};
when Grid is Clicked, 'isPopupOpen' is updated to true.
I use 'react-new-window' library, and this library use 'window.open()' ((https://github.com/rmariuzzo/react-new-window)
so I set different window names to call several popups.
but I can't solve the problem.
I try to set a state object that has a boolean value.
const [popupObj, setPopupObj] = useState({});
when the grid is clicked, popupObj updates like
{'cellA': true, 'cellD': true}
and a return statement is like
{popupObj[cellName] && <Popup /> }
but the result was the same.
what should I do to solve this problem?
I wrote an example for you. Hope it helps.
use popupIds state to store the popups that you want to open
use Set to toggle the popupIds in the addPopup click handler
import * as React from "react";
export default function App() {
const [popupIds, setPopupIds] = React.useState([]);
const addPopup = (popupId) => {
const set = new Set(popupIds);
if (set.has(popupId)) {
set.delete(popupId);
} else {
set.add(popupId);
}
setPopupIds(Array.from(set));
};
return (
<div className="App">
{["hello", "react"].map((popupId) => (
<div onClick={() => addPopup(popupId)}>{popupId}</div>
))}
{popupIds.map((popupId) => (
<Popup title={getPopupTitle(popupId)} />
))}
</div>
);
}
const getPopupTitle = (popupId) => `title for ${popupId}`;
const Popup = ({ title }) => <div>{title}</div>;
Here is a codesandbox that you can play with directly.
You need to add your popup in an array, so you can render many popup as you want, then you need to define in How much time you will remove a added popup from array or add a close button
Extra: you can configure in global state to access in all your application to your popups and you will have a component like this: https://www.npmjs.com/package/notistack

How to set useState while checking propsType

hello I'm trying to set useState but it crashing the whole code. here I'm sending propstype from parent component to child component. so when propstype condition is checked/true then I'm trying to set useState but instead of setting it crashes. please checked below what I tried, Here I've removed unwanted code otherwise it'll too big for readers and they might lost the focus from question. can anyone suggest me any new solution for this I've tried a lot of and then came here for the proper solution.
parent component (BillComponent)
const BillComponent = () => {
return(
{/* Add Bill form */}
<Drawer
title={"Add Bill"}
placement="right"
onClose={onClose}
visible={visible}
className="ant-drawer-half"
>
<AddBill parentCallback={handleCallback} type="add" /> // <---This AddBill is child component and here I'm passing props type as add
</Drawer>
</>
</>
);
};
export default BillComponent;
child component (AddBill)
const AddBill = (props) => {
const [checked, setChecked] = useState(false);
if(props.type === "add"){
setChecked(false); //<---This is where main code is crashing. when I set useState
}else{
setChecked(true);
}
return(
// divs of project
);
};
export default AddBill;
Not sure if that's your case because you didn't show the error. Probably the error will be something related to "Too many re-renders".
It's a bad practice to use those React hooks in the "root" of the component. In your particular case, I'd do the following:
const [checked, setChecked] = useState(false);
useEffect(() => {
if (props.type === "add") {
setChecked(false);
} else {
setChecked(true);
}
}, [props.type]);
Please share the error you're getting.
Also why not just use:
const [checked, setChecked] = useState(props.type === 'add');

Closing Active Element in React Accordion Component

I'm building out a simple accordion component for a product page in my Next.js/react app. I've got it mostly working, however when a user clicks open a new accordion item I need to close the active one. Here's what my component looks like:
import React, { useRef, useState } from 'react';
import css from 'classnames';
import s from './ProductAccordion.module.scss';
interface FeatureProps {
title: string;
copy: string;
}
export const ProductAccordion = ({ content }: any) => {
return (
<div className={s.productAccordion}>
{content.features.map((feature: FeatureProps) => {
const [active, setActive] = useState(false);
const activeClass = active ? 'active' : '';
const toggleAccordion = () => {
setActive(!active);
};
return (
<div
className={css(s.productAccordion__section, s[activeClass])}
key={feature.title}
>
<button className={s.sectionTitle} onClick={toggleAccordion}>
<p className={s.sectionTitle__title}>{feature.title}</p>
<span className={s.button} />
</button>
<div className={css(s.sectionContent, s[activeClass])}>
<div className={s.sectionContent__copy}>{feature.copy}</div>
</div>
</div>
);
})}
</div>
);
};
How can I get my active accordion item to close when a new one is clicked? Thanks!
I would suggest:
moving your useState hook a level higher
instead of "active" being a boolean, make it a string that you can use to identify which item should be active
hopefully a feature has a unique identifier like an id or something that you can use to identify
You could do something like:
export const ProductAccordion = ({ content }: any) => {
const [active, setActive] = useState(''); // set up your useState here, so its value is available to all children elements
return (
<div className={s.productAccordion}>
{content.features.map((feature: FeatureProps) => {
const isActive = active === feature.id // feature.id here is just a stand in for some unique identifier that each feature has
const activeClass = isActive ? 'active' : '';
const toggleAccordion = () => {
if (isActive) {
setActive(''); // if the current item is active, and you toggle it, close the accordian
} else {
setActive(feature.id) // if the current item is not active, and you toggle it, open this section
}
};
return (
<div>
{/* your code here */}
</div>
);
};
Of course, there are many approaches you could take, and I'm sure a more elegant one than this exists. But this should hopefully get you in the right direction!

React / Functional component / Conditional render on callback / Not Working

Why this does not work ?
import React from 'react';
function Room() {
let check = null;
const ibegyouwork = () => {
check = <button>New button</button>;
}
return (
<div>
<button onClick={ibegyouwork}>Display my button now !!!!</button>
{check}
</div>
);
}
export default Room;
And this works fine ?
import React from 'react';
function Room() {
let check = null;
return (
<div>
<button>No need for this button because in this case the second button is auto-displayed</button>
{check}
</div>
);
}
export default Room;
Basically I try to render a component based on a condition. This is a very basic example. But what I have is very similar. If you wonder why I need to update the check variable inside that function is because in my example I have a callback function there where I receive an ID which I need to use in that new component.
The example that I provided to you is basically a button and I want to show another one when I press on this one.
I am new to React and despite I searched in the past 2 hours for a solution I couldn't find anything to address this issue.
Any tips are highly appreciated !
Your component has no idea that something has changed when you click the button. You will need to use state in order to inform React that a rerender is required:
import React, {useState} from 'react'
function Room() {
const [check, setCheck] = useState(null);
const ibegyouwork = () => {
setCheck(<button>New button</button>);
}
return (
<div>
<button onClick={ibegyouwork}>Display my button now !!!!</button>
{check}
</div>
);
}
export default Room;
When you call setCheck, React basically decides that a rerender is required, and updates the view.
The latter is working because there are no changes to the check value that should appear on the DOM.
If check changes should impact and trigger the React render function, you would want to use a state for show/hide condition.
import React from 'react';
const Check = () => <button>New button</button>;
function Room() {
const [show, setShow] = React.useState(false);
const ibegyouwork = () => {
setShow(true);
}
return (
<div>
<button onClick={ibegyouwork}>Display my button now !!!!</button>
{show && <Check />}
</div>
);
}
export default Room;

What is going wrong when I pass a boolean from a child to its parent component using React Hooks?

I'm currently trying to revise the dropdown menu component on my Gatsby site so that it reports a boolean to its parent component, a navbar. I plan on using that boolean to trigger some conditional CSS in Emotion.
The boolean isOpen reports if the dropdown menu is open or not, so true means it's open, and false means it's not.
As of now, I'm using React Hooks to pass that data from the child to the parent component. It seems like I'm successfully passing data, but when I click the dropdown menu, it sends both a true and a false boolean value in rapid succession, even as the menu remains open.
How do I revise this code so that isOpen in the child component is correctly reported to the parent component?
import React, { useState, useEffect } from "react"
const Child = ({ isExpanded }) => {
const [expandState, setExpandState] = useState(false)
useEffect(() => {
setExpandState(isOpen)
isExpanded(expandState)
})
return(
<dropdownWrapper>
<button
{...isExpanded}
/>
{isOpen && (
<Menu>
//menu items go here
</Menu>
)}
</dropdownWrapper>
)
}
const Parent = () => {
const [expandState, setExpandState] = useState(false)
const onExpand = (checkExpand) => {
setExpandState(checkExpand)
}
return(
<Dropdown
isExpanded={onExpand}
onClick={console.log(expandState)}
/>
)
}
Figured this one out myself. Parent needed a useEffect to register the incoming boolean.
Fixed code for the parent:
const Parent = () => {
const [expandState, setExpandState] = useState(false)
const onExpand = (checkExpand) => {
setExpandState(checkExpand)
}
useEffect(() => {
onExpand(expandState)
})
return(
<Dropdown
isExpanded={onExpand}
onClick={console.log(expandState)}
/>
)
}

Categories

Resources