How can I dynamically update className with React js? - javascript

I have a Collection component and when I click on a div in this component, I want to change both the className of the Collection component and the className of the first sibling component after the Collection component.
With UseState, I could only change the className of the component I was in.
My Collection.js looks like this:
const Collection = () => {
const [toggleClass, setToggleClass] = useState(false);
function handleClick() {
setToggleClass((toggleClass) => !toggleClass);
}
let toggleClassCheck = toggleClass ? "passive" : "active";
return (
<React.Fragment>
<div className={`step ${toggleClassCheck}`}>
<div className="atrium">
<span>Collection</span>
</div>
<div className="content">
<div
className="moreThenOne"
area="collectionTitle"
onClick={handleClick}
>
Can anyone help me on how to do the process I mentioned above?

I didn't quite understand what you want, but if you want to impact sibling of component "Collection" executing function inside of "Collection" you definitely should try "ref". Via useRef hook.
export const Collection = () => {
const [toggleClass, setToggleClass] = useState(false);
const divRef = useRef(null);
function handleClick() {
divRef.current.nextSibling.classList.toggle('active');
divRef.current.nextSibling.classList.toggle('passive');
setToggleClass((toggleClass) => !toggleClass);
}
let toggleClassCheck = toggleClass ? 'passive' : 'active';
return (
<>
<div className={`step ${toggleClassCheck}`} ref={divRef}>
<div className="atrium">
<span>Collection</span>
</div>
<div className="content">
<div className="moreThenOne" onClick={handleClick}>
CLICK ZONE
</div>
</div>
</div>
</>
);
};

The React components rerender, based on the state. So you sould use your state to define the className instead of a boolean.
When your state changes the component will re-render.
Try this:
const Collection = () => {
const [toggleClass, setToggleClass] = useState("active");
function handleClick() {
setToggleClass((prevState) => prevState == "active" ? "passive" : "active" );
}
return (
<React.Fragment>
<div className={`step ${toggleClass}`}>
<div className="atrium">
<span>Collection</span>
</div>
<div className="content">
<div
className="moreThenOne"
area="collectionTitle"
onClick={handleClick}
>

Related

React callback to parent not changing state

I cannot for the life of me figure out why this isn't working. I have a WorkoutCard component:
WorkoutCard:
const key = require('weak-key');
function WorkoutCard({ workout }) {
const { userID } = useContext(AuthContext);
const [modalOpen, setModalOpen] = useState(false);
const closeModalCallback = () => setModalOpen(false);
return (
<div className="WorkoutCard" onClick={() => setModalOpen(true)}>
<div className="header">
<h2>{workout.name}</h2>
<h3>Days</h3>
{workout.days.map((day) => {
return (
<p key={key({})}>{day}</p>
)
})}
<button className="deleteButton" onClick={handleDelete}>Delete</button>
</div>
<div className="line"></div>
<div className="exercises">
<h3>Exercises</h3>
{workout.exercises.map((exercise) => {
return (
<p key={key({})}>{exercise}</p>
)
})}
</div>
<EditWorkoutModal key={key({})} modalOpen={modalOpen} closeModalCallback={closeModalCallback} />
</div>
);
}
export default WorkoutCard;
And I have the EditWorkoutModal component:
Modal.setAppElement('#root');
function EditWorkoutModal({ modalOpen, workout, closeModalCallback }) {
const { userID } = useContext(AuthContext);
return (
<Modal isOpen={modalOpen} className="editModal">
<div className="rightHalf">
<p className='closeButton' onClick={() => closeModalCallback()}>+</p>
</div>
</Modal>
)
}
export default EditWorkoutModal
The problem here is that closeModalCallback is not changing the state whatsoever. It is called, but modalOpen is still set to true.
And, this is even more confusing, because I have this functionality working in another part of the app. I have a workouts page that has both WorkoutCard components, as well as a Modal, and it works this way. However, the closeModalCallback on the WorkoutCard components' modals will not work.
onClick events bubble up the DOM. For example, see the below snippet (see browser console for output):
const App = () => {
const parent = () => console.log("parent");
const child = () => console.log("child");
return <div onClick={parent}>
<div onClick={child}>Click me</div>
</div>
}
ReactDOM.createRoot(document.body).render(<App />);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.0.0/umd/react-dom.production.min.js"></script>
The above logs the following in the console:
> child
> parent
When you click on your child element, the click event bubbles up the DOM, eventually reaching your div with the WorkoutCard class, which fires the onClick that sets your modal-open state to true. You can stop the event from bubbling by calling e.stopPropagation() on your close-modal button:
onClick={(e) => {
e.stopPropagation();
closeModalCallback();
}}
This way the event won't bubble up to your parent div and trigger the onClick which is changing your state.

How to change component that is rendered?

I'm super new to React.js. I'm making changeable layouts using React.js. so i tried to use useState for rendering specific layout that I should click. so I tried to add setState for changing false in a function and made one another setState in the same function. but Too many re-renders Error came out. so what can i use for making changeable layout??
this is my code
import React, { useState } from "react";
import Panel from "./Panel";
import PanelTwo from "./PanelTwo";
import styled from "styled-components";
export default function Layout() {
const [showPanel, setShowPanel] = useState(false);
const [showPanel1, setShowPanel1] = useState(false);
const handleOnClick = () => setShowPanel(true);
const handleOnClick1 = () => setShowPanel1(true);
return (
<div>
<Main>
<div onClick={handleOnClick}>
<h1>Panel (1+3)</h1>
</div>
<div onClick={handleOnClick1}>
<h1>Panel (2+2) </h1>
</div>
<div>
<h1>Panel (2+3)</h1>
</div>
<div>
<h1>Panel (2+4)</h1>
</div>
<div>
<h1>Panel (3+1)</h1>
</div>
<div>
<h1>Panel (3+2)</h1>
</div>
</Main>
{showPanel ? <Panel /> : null}
{showPanel1 ? <PanelTwo /> : null}
</div>
);
}
and if i do this, it looks like this when i click two buttons
enter image description here
so i tried this .
const [showPanel, setShowPanel] = useState(false);
const [showPanel1, setShowPanel1] = useState(false);
const handleOnClick = () => setShowPanel(true);
const handleOnClick1 = () => setShowPanel(false);
setShowPanel1(true);
and i got Too many re-renders Error.
this is what i want to do
enter image description here
when i click each buttons then the exact only one layout is gonna show up .
but the problem is that it's overlapped when i click two buttons
like this
enter image description here
I'm sorry if my explanation is not good.
It seems like you really just want to toggle between the two layouts. You can do this with a single state value, callback, and conditional render via ternary.
export default function Layout() {
const [showPanel, setShowPanel] = useState(false);
const handleOnClick = () => setShowPanel(show => !show);
return (
<div>
<Main>
<div onClick={handleOnClick}>
<h1>Panel (1+3)</h1>
</div>
<div onClick={handleOnClick}>
<h1>Panel (2+2) </h1>
</div>
...
</Main>
{showPanel ? <Panel /> : <PanelTwo />}
</div>
);
}
OFC, this assumes you want to always show at least one of the layouts. If you want to start with both initially hidden (i.e. false) then you can toggle the other panel state false in the handlers to do that.
export default function Layout() {
const [showPanel, setShowPanel] = useState(false);
const [showPanel1, setShowPanel1] = useState(false);
const handleOnClick = () => {
setShowPanel(true);
setShowPanel1(false);
};
const handleOnClick1 = () => {
setShowPanel(false);
setShowPanel1(true);
};
return (
<div>
<Main>
<div onClick={handleOnClick}>
<h1>Panel (1+3)</h1>
</div>
<div onClick={handleOnClick1}>
<h1>Panel (2+2) </h1>
</div>
...
</Main>
{showPanel && <Panel />}
{showPanel1 && <PanelTwo />}
</div>
);
}

React useEffect Problems

So here is the problem which I can't seem to solve. I have an app component, inside of App I have rendered the Show Component. Show component has toggle functionality as well as a outside Click Logic. In the Show component I have a Button which removes an item based on his Id, problem is that When I click on the button Remove. It removes the item but it also closes the Show Component, I don't want that, I want when I press on button it removes the item but does not close the component. Thanks
App.js
const App =()=>{
const[isShowlOpen, setIsShowOpen]=React.useState(false)
const Show = useRef(null)
function openShow(){
setIsShowOpen(true)
}
function closeShowl(){
setIsShowOpen(false)
}
const handleShow =(e)=>{
if(show.current&& !showl.current.contains(e.target)){
closeShow()
}
}
useEffect(()=>{
document.addEventListener('click',handleShow)
return () =>{
document.removeEventListener('click', handleShow)
}
},[])
return (
<div>
<div ref={show}>
<img className='taskbar__iconsRight' onClick={() =>
setIsShowOpen(!isShowOpen)}
src="https://winaero.com/blog/wp-content/uploads/2017/07/Control-
-icon.png"/>
{isShowOpen ? <Show closeShow={closeShow} />: null}
</div>
)
}
Show Component
import React, { useContext } from 'react'
import './Show.css'
import { useGlobalContext } from '../../context'
import WindowsIcons from '../../WindowsIcons/WindowsIcons'
import { GrClose } from 'react-icons/gr'
const Show = ({closeShow}) => {
const {remove, icons }= useGlobalContext()
}
return (
<div className='control__Panel'>
<div className='close__cont'>
<GrClose className='close' onClick={closeShow} />
<h3>Show</h3>
</div>
<div className='control__cont'>
{icons.map((unin)=>{
const { name, img, id} = unin
return (
<li className='control' key ={id}>
<div className='img__text'>
<img className='control__Img' src={img} />
<h4 className='control__name'>{name}</h4>
</div>
<button className='unin__button' onClick={() => remove(id)} >remove</button>
</li> )
})}
</div>
</div>
)
}
export default Show
Try stop propagation function, it should stop the propagation of the click event
<button
className='unin__button'
onClick={(e) => {
e.stopPropagation();
remove(id);
}}
>remove</button>
You have a few typos in your example. Are they in your code? If they are, you're always reach the closeShow() case in your handler, since you're using the wrong ref.
const App =()=>{
const[isShowOpen, setIsShowOpen]=React.useState(false) <<-- Here 'isShowlOpen'
const show = useRef(null) <<-- here 'S'how
function openShow(){
setIsShowOpen(true)
}
function closeShow(){ <<-- Here 'closeShowl'
setIsShowOpen(false)
}
const handleShow =(e)=>{
if(show.current&& !show.current.contains(e.target)){ <<-- here 'showl'
closeShow()
}
}
useEffect(()=>{
document.addEventListener('click',handleShow)
return () =>{
document.removeEventListener('click', handleShow)
}
},[])
return (
<div>
<div ref={show}>
<img className='taskbar__iconsRight' onClick={() =>
setIsShowOpen(!isShowOpen)}
src="https://winaero.com/blog/wp-content/uploads/2017/07/Control-
-icon.png"/>
{isShowOpen ? <Show closeShow={closeShow} />: null}
</div>
)
}

useState passed to useContext not updating state

I am trying to use useContext to create a generic Tooltip component that passes a close() function to the content inside the Tooltip. I have written my Tooltip like this
export function Tooltip(props) {
const [active, setActive] = useState(false);
const close = () => {
setActive(false);
}
return (
<div className="tooltip-wrapper"
onClick={() => setActive(true)}
>
{props.children}
<TooltipContext.Provider value={{close}}>
{active && (
<div className='tooltip-tip bottom' ref={node}>
{props.content}
</div>
)}
</TooltipContext.Provider>
</div>
)
}
I create the Tooltip in a different class component as follows
function Category(props) {
return (
<Tooltip content={<AddCategoryInnerTooltip name={props.name}/>}>
<p className="tooltip-name-opener">{props.name}</p>
</Tooltip>
);
}
function AddCategoryInnerTooltip(props) {
const {close} = useContext(TooltipContext);
return(
<div className="inner-tooltip-wrapper">
<input
className="tooltip-custom-input"
type="text"
defaultValue={props.name}
/>
<div className="button-end">
<button onClick={close}>Cancel</button>
<button>Ok</button>
</div>
</div>
)
}
When I attempt to call close within the AddCategoryInnerTooltip, the state passed from the Tooltip component doesn't update. When I console.log the state, it always comes as true without changing. What am I doing wrong?
should be a callback function
<button onClick={()=>close}>Cancel</button>

how to access a component style in react js

I have a div tag I want to render only if renderCard() style overflow is scroll. I have tried a renderCard().style.overflow which does not seem to target this
Edit: renderCard added
const SearchCard = () => (
<button class="invisible-button" onClick={onSearchCardClick}>
//
</button>
);
const AnswerCard = () => (
<div className="results-set">
//
</div>
);
const renderCard = () => {
if (card && card.answer) {
return AnswerCard();
} else if (card) {
return SearchCard();
}
return null;
};
<React.Fragment>
<div id="search-results">{renderCard()}</div>
{renderFollowup ? null : (
<React.Fragment>
<div id="search-footer">
{
(renderCard().style.overflow = "scroll" ? (
<div className="scroll-button">
<a href="#bottomSection">
<img src="images/arrow_down.svg" alt="scroll to bottom" />
</a>
</div>
) : null)
}
</div>
</React.Fragment>
)}
</React.Fragment>
);
};
To do this you need a ref to the actual DOM element rendered by renderCard().
renderCard() here returns a React element which doesn't have the style property or any other DOM properties on it - it's just a React representation of what the DOM element will eventually be once rendered - hence you need to get the actual DOM element via a ref where you'll have access to this and other properties.
Example code below using useRef to create the ref that will be attached to the element with the style you need to access. Note how useEffect is used to access the ref's value because it's only available after the first render when the DOM element is present.
const Example = () => {
const ref = React.useRef()
React.useEffect(() => {
alert('overflow value is: ' + ref.current.style.overflow)
}, [])
return (
<div ref={ref} style={{ overflow: 'scroll' }}>hello world</div>
)
}
ReactDOM.render(<Example />, document.getElementById('root'))
<script crossorigin src="https://unpkg.com/react#16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Refactor the cards to be actual rendered components, pass the ref and attach to the elements
const SearchCard = ({ overflowRef }) => (
<button ref={overflowRef} class="invisible-button" onClick={onSearchCardClick}>
//
</button>
);
const AnswerCard = ({ overflowRef }) => (
<div ref={overflowRef} className="results-set">
//
</div>
);
const RenderCard = ({ overflowRef }) => {
if (card && card.answer) {
return <AnswerCard overflowRef={overflowRef} />;
} else if (card) {
return <SearchCard overflowRef={overflowRef} />;
}
return null;
};
In the component rendering them, create a ref using either createRef or useRef react hook if it is a functional component
const overflowRef = createRef();
or
const overflowRef = useRef();
Pass the ref to RenderCard, to be passed on
<RenderCard overflowRef={overflowRef} />
And then check the overflow value as such
overflowRef.current.style.overflow === "scroll"
With the approach above you might want to refactor some of the HoC components and pass a function from parent to child that returns the ref of the element to be accessed also I am not sure of the scope of the block of code where you are executing the ternary.
Perhaps a simpler hack is to rely on using a useState hook with vanilla DOM selectors and passing that into the or even better, just add it to the stateless func component that wraps React.Fragment as a hook:
const myComponent = () => {
const [hasScrollOverflow, setHasScrollOverflow] = useState(false);
React.useEffect(() => {
const element = document.querySelector(".results-set");
const elementStyle = element.style;
if (elementStyle.getPropertyValue('overflow') === 'scroll') {
setHasScrollOverflow(true);
}
}, [])
return (
<React.Fragment>
<div id="search-results">{renderCard()}</div>
{renderFollowup ? null : (
<React.Fragment>
<div id="search-footer">
{hasScrollOverflow ? (
<div className="scroll-button">
<a href="#bottomSection">
<img src="images/arrow_down.svg" alt="scroll to bottom" />
</a>
</div>
) : null}
</div>
</React.Fragment>
)}
</React.Fragment>
);
};

Categories

Resources