Split one react component into two - reactjs

I would like to know how I should split this code into two separate components.
import React from "react";
const DropdownMenu = (props) => {
const DropdownItem = (props) => {
return (
<a href="#" className="menu-item">
<span className="icon-button">{props.rightIcon}</span>
{props.children}
</a>
);
};
return (
<div className="dropdown">
<DropdownItem>{props.companyName[0].name}</DropdownItem>
<DropdownItem>{props.companyName[1].name}</DropdownItem>
<DropdownItem>Settings</DropdownItem>
<DropdownItem>Testing</DropdownItem>
<DropdownItem>Get our App</DropdownItem>
<DropdownItem>Mobile</DropdownItem>
<DropdownItem>Log out</DropdownItem>
</div>
);
};
export default DropdownMenu;
I try to split them up by myself but then my dropdown menu breaks and instead of opening a container down, it opens container inside my navbar, I think the issue with {props.children} but I don't know exactly how to fix it.
After splitting components I want it to remain like on this screenshot

there is 2 problems with your code.
well first, don't ever create a component inside render function of another component.
second and root cause of your problem is you have 2 variables named "props" in same scope
const DropdownItem = (props) => {
return (
<a href="#" className="menu-item">
<span className="icon-button">{props.rightIcon}</span>}
{props.children}
</a>
);
};
const DropdownMenu = (props) => {
return (
<div className="dropdown">
<DropdownItem>{props.companyName[0].name}</DropdownItem>
<DropdownItem>{props.companyName[1].name}</DropdownItem>
<DropdownItem>Settings</DropdownItem>
<DropdownItem>Testing</DropdownItem>
<DropdownItem>Get our App</DropdownItem>
<DropdownItem>Mobile</DropdownItem>
<DropdownItem>Log out</DropdownItem>
</div>
);
};
export default DropdownMenu
that should fix it.

you should pass rightIcon to every DropdownItem component that it`s passed to it and better to named your props instead of passing them as children
first component:
const DropdownItem = ({ label, rightIcon=' ', href='' }) => {
return (
<a href={href} className="menu-item">
<span className="icon-button">{rightIcon}</span>
{label}
</a>
);
};
second component:
const DropdownMenu = (props) => {
return (
<div className="dropdown">
<DropdownItem
label={props?.companyName?.[0]?.name}
rightIcon="icon-name"
href="href for this item"
/>
<DropdownItem label={props?.companyName?.[1]?.name} rightIcon="second-icon-name"/>
<DropdownItem label="Settings" />
<DropdownItem label="Testing" />
<DropdownItem label="Get our App" />
<DropdownItem label="Mobile" />
<DropdownItem label="Log out" rightIcon='log-out-icon'/>
</div>
);
}

Related

How to open dynamic modal with react js

I am trying to convert the HTML/Javascript modal to React js.
In Reactjs, I just want to open the modal whenever the user clicks the View Project button.
I have created a parent component (Portfolio Screen) and a child component (Portfolio Modal). The data I have given to the child component is working fine but the modal opens the first time only and then does not open. Another problem is that the data does not load even when the modal is opened the first time.
Codesandbox link is here.
https://codesandbox.io/s/reverent-leftpad-lh7dl?file=/src/App.js&resolutionWidth=683&resolutionHeight=675
I have also shared the React code below.
For HTML/JavaScript code, here is the question I have asked before.
How to populate data in a modal Popup using react js. Maybe with hooks
Parent Component
import React, { useState } from 'react';
import '../assets/css/portfolio.scss';
import PortfolioModal from '../components/PortfolioModal';
import portfolioItems from '../data/portfolio';
const PortfolioScreen = () => {
const [portfolio, setportfolio] = useState({ data: null, show: false });
const Item = (portfolioItem) => {
setportfolio({
data: portfolioItem,
show: true,
});
};
return (
<>
<section className='portfolio-section sec-padding'>
<div className='container'>
<div className='row'>
<div className='section-title'>
<h2>Recent Work</h2>
</div>
</div>
<div className='row'>
{portfolioItems.map((portfolioItem) => (
<div className='portfolio-item' key={portfolioItem._id}>
<div className='portfolio-item-thumbnail'>
<img src={portfolioItem.image} alt='portfolio item thumb' />
<h3 className='portfolio-item-title'>
{portfolioItem.title}
</h3>
<button
onClick={() => Item(portfolioItem)}
type='button'
className='btn view-project-btn'>
View Project
</button>
</div>
</div>
))}
<PortfolioModal portfolioData={portfolio} show={portfolio.show} />
</div>
</div>
</section>
</>
);
};
export default PortfolioScreen;
Child Component
import React, { useState, useEffect } from 'react';
import { NavLink } from 'react-router-dom';
const PortfolioModal = ({ portfolioData, show }) => {
const portfolioItem = portfolioData;
const [openModal, setopenModal] = useState({ showState: false });
useEffect(() => {
setopenModal({
showState: show,
});
}, [show]);
return (
<>
<div
className={`portfolio-popup ${
openModal.showState === true ? 'open' : ''
}`}>
<div className='pp-inner'>
<div className='pp-content'>
<div className='pp-header'>
<button
className='btn pp-close'
onClick={() =>
setopenModal({
showState: false,
})
}>
<i className='fas fa-times pp-close'></i>
</button>
<div className='pp-thumbnail'>
<img src={portfolioItem.image} alt={`${portfolioItem.title}`} />
</div>
<h3 className='portfolio-item-title'>{portfolioItem.title}</h3>
</div>
<div className='pp-body'>
<div className='portfolio-item-details'>
<div className='description'>
<p>{portfolioItem.description}</p>
</div>
<div className='general-info'>
<ul>
<li>
Created - <span>{portfolioItem.creatDate}</span>
</li>
<li>
Technology Used -
<span>{portfolioItem.technologyUsed}</span>
</li>
<li>
Role - <span>{portfolioItem.Role}</span>
</li>
<li>
View Live -
<span>
<NavLink to='#' target='_blank'>
{portfolioItem.domain}
</NavLink>
</span>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</>
);
};
export default PortfolioModal;
You don't have to use one useState hook to hold all your states. You can and I think you should break them up. In the PortfolioScreen component
const [data, setData] = useState(null);
const [show, setShow] = useState(false);
I changed the function Item that is used to set the active portfolio item to toggleItem and changed it's implementation
const toggleItem = (portfolioItem) => {
setData(portfolioItem);
setVisible(portfolioItem !== null);
};
You should use conditional rendering on the PortfolioModal, so you won't need to pass a show prop to it, and you'll pass a closeModal prop to close the PortfolioModal when clicked
{visible === true && data !== null && (
<PortfolioModal
data={data}
closeModal={() => toggleItem()} // Pass nothing here so the default value will be null and the modal reset
/>
)}
Then in the PortfolioModal component, you expect two props, data and a closeModal function
const PortfolioModal = ({ data, closeModal }) => {
And the close button can be like
<button className="btn pp-close" onClick={closeModal}>
...

React - toggleClass on button activate all buttons

I'm trying to make navigation menu similiar to the nav menu in reactjs.org
I'm using Header component and navigation which is objects with links and name. I'm adding class onClick using the state but this toggle all buttons.
import React, { useState } from "react";
import styles from "./index.module.css";
import getNavigation from "../../utils/navigation";
import { Link } from "react-router-dom";
import logo from "../../images/europa-logo.png";
const Header = () => {
const links = getNavigation();
const [isActive, setActive] = useState(false);
const toggleClass = () => {
setActive(!isActive);
};
return (
<div>
<nav className={styles.topnav}>
<div className={styles.pageWrapper}>
<img src={logo} alt="Logo" />
<ul>
{links.map((l, i) => (
<li key={i}>
<Link
className={isActive ? "btn-active" : null}
onClick={toggleClass}
to={l.link}
value={l.title}
>
{l.title}
</Link>
</li>
))}
<li>
{" "}
<div className={styles.social}>
<a href="https://facebook.com">
<FontAwesomeIcon
size="2x"
icon={["fab", "facebook-square"]}
/>{" "}
</a>
<a href="mailto:someone#mail.com">
<FontAwesomeIcon size="2x" icon="envelope" />
</a>
</div>
</li>
</ul>
</div>
</nav>
</div>
);
};
export default Header;
The result is all buttons are activated:
My goal is to activate only the link which is clicked and the first button in nav menu need to be activated by default. What I'm doing wrong?
You can use <NavLink> instead of simple <Link>
is a special version of the that will add styling attributes to the rendered element when it matches the current URL.
<NavLink to="/" activeClassName="active">Link</NavLink>
You can check the docs here:
https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/docs/api/NavLink.md
You can define active index and your condition look like this
className={activeIndex === i ? "btn-active" : ""}
Toggle class function:
const [activeIndex, setActiveIndex] = useState(0);
const toggleClass = (i) => {
setActiveIndex(i);
};
and onClick will look like this
onClick={()=>{toggleClass(i);}}

Passing class name down from one component to another in React

I have two files, header.js and toggle.js. I'm trying to change the class name of one of the elements in the parent component.
How can I add the active class to my <ul className="nav-wrapper"> when the button is clicked?
Here's my code:
header.js
const Header = ({ siteTitle, menuLinks, }) => (
<header className="site-header">
<div className="site-header-wrapper wrapper">
<div className="site-header-logo">
<Link to="/" className="brand">Brand Logo</Link>
</div>
<div className="site-header-right">
<nav className="nav">
<Toggle />
<ul className="nav-wrapper">
{menuLinks.map(link => (
<li
key={link.name}
className="nav-item"
>
<Link to={link.link}>
{link.name}
</Link>
</li>
))}
</ul>
</nav>
</div>
</div>
</header>
)
Header.propTypes = {
siteTitle: PropTypes.string,
}
Header.defaultProps = {
siteTitle: ``,
}
export default Header
toggle.js
export default function Toggle() {
const [isActive, setActive] = useState("false");
const handleToggle = () => {
setActive(!isActive);
};
return (
<button
className="nav-toggle"
className={isActive ? "app" : null}
onClick={handleToggle}
aria-expanded="false"
type="button">
MENU
</button>
);
}
Thanks for any help!
Easiest way will be refactoring your code to have the useState in the Header component and then passing that state to your Toggle component as props. This will make the isActive prop available in the header so you can do something like this:
const Header = ({ siteTitle, menuLinks, }) => {
const [isActiveNav, setActiveNav] = useState(false);
const activeClass = isActiveNav ? 'active' : ''
return (
// All your jsx
<Toggle isActive={isActiveNav} setActive={setActiveNav} />
<ul className={`nav-wrapper ${activeClass}`}>
{// More JSX}
</ul>
)
Now in your Toggle component
export default function Toggle({ isActive, setActive }) {
const handleToggle = () => {
setActive(!isActive);
};
return (
<button
className="nav-toggle"
className={isActive ? "app" : ''}
onClick={handleToggle}
aria-expanded={isActive}
type="button">
MENU
</button>
);
}
I did some changes in your code:
Don't use null as a className, use an empty string instead
The useState value should be false not "false".
You can pass the isActive value to the aria-expanded prop.
This will do the trick, and is the easiest approach.

Hiding an element after a while in Next.js

I have a header component as a function component. I want show a popup when logo text is clicked. After for a time it should close automatically. I use hooks for state of popup. But set state function doesn't work in setTimeout function. How can fix this?
import Link from 'next/link'
import style from './header.module.css'
const Header = () => {
const [popupOpen, setPopupOpen] = React.useState(false)
return (
<header className={style.header}>
<nav className={style.nav}>
<div
className={style.popupContainer}
onClick={() => {
setPopupOpen(!popupOpen)
console.log(popupOpen)
setTimeout(() => {
console.log(popupOpen)
setPopupOpen(!popupOpen)
console.log(popupOpen)
}, 1000)
}}
>
<span className={style.logo}>Logo</span>
<span
className={`${style.popupText} ${
popupOpen ? style.show : style.hide
}`}
>
Popup Text
</span>
</div>
<ul className={style.ul}>
<li>
<Link href='/'>
<a>.home</a>
</Link>
</li>
<li>
<Link href='/contact'>
<a>.contact</a>
</Link>
</li>
</ul>
</nav>
</header>
)
}
export default Header
Console log:
Let me suggests, this is the same question as:
React - useState - why setTimeout function does not have latest state value?
const _onClick = () => {
setPopupOpen(!popupOpen);
setTimeout(() => {
setPopupOpen(popupOpen => !popupOpen)
}, 2000);
};
Its happening because setPopupOpen is asynchronous. So by the time setPopupOpen(!popupOpen) is called it has same value as onClick first setPopupOpen(!popupOpen) so eventually when it called both setPopup doing same state update i.e both updating as false. Better way is to usesetPopupOpen callback function to update the value. I added this code.
import { useState } from "react";
import Link from "next/link";
import style from "./style.module.css";
const Header = () => {
const [popupOpen, setPopupOpen] = useState(false);
const toggle = () => {
setPopupOpen((prev) => !prev);
};
const onClick = () => {
setPopupOpen(!popupOpen);
setTimeout(() => {
toggle();
}, 1000);
};
return (
<header className={style.header}>
<nav className={style.nav}>
<div className={style.popupContainer} onClick={onClick}>
<span className={style.logo}>Logo</span>
{popupOpen && (
<span
className={`${style.popupText} ${
popupOpen ? style.show : style.hide
}`}
>
Popup Text
</span>
)}
</div>
<ul className={style.ul}>
<li>
<Link href="/">
<a>.home</a>
</Link>
</li>
<li>
<Link href="/contact">
<a>.contact</a>
</Link>
</li>
</ul>
</nav>
</header>
);
};
export default function IndexPage() {
return (
<div>
<Header />
</div>
);
}
Here is the demo: https://codesandbox.io/s/pedantic-haibt-iqecz?file=/pages/index.js:0-1212

Style properties on react components

I have a component that is being repeated and the only reason its being repeated is because of the icon, color, and text.
import React from 'react';
const TabOne = () =>{
return (
<div className="irequest-tabs users">
<span className="tabOne"><i className="fa fa-sitemap navbuttonIcon" aria-hidden="true"></i>
<span className="hidden-sm hidden-xs">I want to...</span></span>
</div>
);
}
export default TabOne
I wanted to see if there was a way to make those properties on the component so that my component when used would look like:
<Tab1 color="#fff" icon="fa fa-sitemap" text="I want too..." />
I've tried:
import React from 'react';
const TabOne = () =>{
return (
<div style={{color: props.color}} className="irequest-tabs users">
<span className="tabOne"><i className={props.icon} className="navbuttonIcon" aria-hidden="true"></i>
<span className="hidden-sm hidden-xs">{props.text}</span></span>
</div>
);
}
export default TabOne
Almost there, just pass props as argument to TabOne (I'll simplify your markup for readability).
const TabOne = (props) => {
return (
<div style={{color: props.color}}>
<i className={props.icon}></i>
<span>{props.text}</span>
</div>
);
}
export default TabOne;
Remember, a functional component gets the properties passed by its parent as args to the function.

Resources