I'm making a menu bar and I've already made it. And This is the code I made. codesandbox
But, as you can see too many states were used. So I want to do refactoring the code by using map function.
I succeeded in loading each data. But don't know how to handle state separately.
I only declared one state and want it to be changed separately when it is hovered.
Question
How can I handle state separately when I use map function??
Originally, the code has a click function, but now I'll ask it to work only when it is hovered.
I think you'll know what I'm asking if you look at the codesandbox below. Thanks
code
import { useState } from "react";
import styled from "styled-components";
import dummy from "./database/DB.json";
export default function App() {
const [data, setData] = useState(dummy.products);
const [hoverCharacter, setHoverCharacter] = useState(false);
return (
<Wrap>
<div className="characterList">
<div className="word">
<h2 className="word__font">Like Pick!</h2>
</div>
<div className="list">
<ul className="list__ul">
{data.map((item) => {
return (
<div className="list__box">
<Card
hoverCharacter={hoverCharacter}
setHoverCharacter={setHoverCharacter}
key={item.id}
item={item}
/>
</div>
);
})}
</ul>
</div>
</div>
</Wrap>
);
}
function Card({ item, hoverCharacter, setHoverCharacter }) {
return (
<CardWrap item={item}>
<div
onMouseEnter={() => {
setHoverCharacter(true);
}}
onMouseLeave={() => {
setHoverCharacter(false);
}}
className={`list__wrap ${hoverCharacter ? "active" : ""}`}
>
<div className={`list__img ${hoverCharacter ? "active" : ""}`} />
{item.nameKr}
</div>
</CardWrap>
);
}
const Wrap = styled.div`
position: relative;
top: calc(50vh - 100px);
width: 1200px;
display: inline-flex;
.characterList {
}
.word {
margin: 0 auto;
width: 1200px;
}
.word__font {
font-family: "SFCompactDisplay-Bold", sans-serif;
font-weight: bold;
font-size: 38px;
margin-bottom: 25px;
text-align: center;
}
.list {
margin: 0 auto;
width: 1200px;
padding-bottom: 20px;
}
.list__ul {
display: inline-flex;
list-style: none;
}
.list__box {
margin: 0 9px;
text-align: center;
font-size: 17px;
}
`;
const CardWrap = styled.div`
.list__wrap {
color: #999;
font-size: 13px;
display: inline-block;
text-align: center;
}
.list__wrap.active {
color: #333;
font-size: 13px;
display: inline-block;
text-align: center;
}
.list__img {
background-image: url(https://www.kakaofriendsgolf.com/img/v3_img_sprites_friends#3x.png);
background-repeat: no-repeat;
width: 70px;
height: 70px;
background-size: 100%;
margin-bottom: 5px;
background-position: 0 ${(props) => props.item.greyed}%;
}
.list__img.active {
background-image: url(https://www.kakaofriendsgolf.com/img/v3_img_sprites_friends#3x.png);
background-repeat: no-repeat;
width: 70px;
height: 70px;
background-size: 100%;
margin-bottom: 5px;
background-position: 0 ${(props) => props.item.blacked}%;
}
`;
json file
{
"products": [
{
"id": "1",
"name": "choosik",
"nameKr": "춘식이",
"greyed": 17.647059,
"blacked": 11.764706
},
{
"id": "2",
"name": "ryan",
"nameKr": "라이언",
"greyed": 88.235295,
"blacked": 82.352941
},
{
"id": "3",
"name": "apeach",
"nameKr": "어피치",
"greyed": 5.882353,
"blacked": 0
}
]
}
codesandbox
https://codesandbox.io/s/characterselectmap-t2mqef?file=/src/App.js:0-2857
Related
I am running into an issue with getting SimpleLightbox not loading for me until I refresh the page on my Gatsby site. I have a demo up where if you go from the "inspirations" page (https://effulgent-liger-a1d01e.netlify.app/inspiration) then click on an individual inspiration you will see by clicking on the photo in the gallery that it just opens the photo url and not the lightbox. Then if you refresh the page and click an image in the gallery it then loads the lightbox fine. I assume this is part of my Gatsby learning process, but stumped on this. Thanks for any help.
Here is my individual "inspiration" post file code.
import React from 'react'
import styled from 'styled-components'
import { GatsbyImage } from 'gatsby-plugin-image'
import SimpleLightbox from 'simple-lightbox'
import 'simple-lightbox/dist/simpleLightbox.min.css'
import Seo from "../components/seo"
import CTAOne from '../components/ctaOne'
const HeroSection = styled.section`
height: 650px;
position: relative;
#media(min-width: 768px) {
height: 825px;
}
.hero-image .gatsby-image-wrapper {
width: 100%;
height: 650px;
object-fit: cover;
object-position: center;
#media(min-width: 768px) {
height: 825px;
}
}
.hero-content {
position: absolute;
bottom: 40px;
left: 50%;
transform: translateX(-50%);
z-index: 1;
text-align: center;
width: calc(100% - 20px);
max-width: 1160px;
padding: 15px 0;
background-color: var(--white-trans);
.hero-border-top,
.hero-border-bottom {
height: 18px;
width: 100%;
border: 6px solid var(--white-trans);
}
.hero-border-top {
margin-top: -33px;
}
.hero-border-bottom {
margin-bottom: -33px;
}
h1 {
margin-bottom: 0;
}
p {
font-size: 21px;
line-height: 1.2;
width: 100%;
max-width: 900px;
margin: 25px auto 21px auto;
padding-left: 10px;
padding-right: 10px;
#media(min-width: 768px) {
font-size: 23px;
}
}
}
`
const InspirationPostContainer = styled.section`
margin-bottom: 90px;
.inspiration-desc {
max-width: 850px;
margin: 90px auto 85px auto;
text-align: center;
}
.inspiration-grid {
column-count: 1;
column-gap: 55px;
#media(min-width: 640px) {
column-count: 2;
}
#media(min-width: 992px) {
column-count: 3;
}
}
.item {
margin: 0 0 40px;
display: grid;
grid-template-rows: 1fr auto;
break-inside: avoid;
}
.item > img {
grid-row: 1 / -1;
grid-column: 1;
}
`
function InspirationPost({ pageContext }) {
const { inspiration } = pageContext
if (typeof window !== `undefined`) {
new SimpleLightbox({elements: '.inspiration-grid a'})
}
return (
<>
<HeroSection>
<div className='hero-image'>
<GatsbyImage image={inspiration.inspiration.projectFeaturedPhoto.gatsbyImage} alt={inspiration.inspiration.projectFeaturedPhoto.altText} />
</div>
<div className="hero-content">
<div className='hero-border-top'></div>
<h1>{inspiration.inspiration.projectTitle}</h1>
<div className='hero-border-bottom'></div>
</div>
</HeroSection>
<InspirationPostContainer>
<div className="container">
<div className='inspiration-desc'>{inspiration.inspiration.projectDescription}</div>
<h2>Project Gallery</h2>
<div className='inspiration-grid'>
{inspiration.inspiration.projectGallery.map((node) => (
<a href={`${node.mediaItemUrl}`} className='item' key={node.id}>
<GatsbyImage image={node.gatsbyImage} alt={node.altText} />
</a>
))}
</div>
</div>
</InspirationPostContainer>
<CTAOne />
</>
)
}
export default InspirationPost
The issue seems to be related to the SimpleLightbox module. Have you tried something like this?
useEffect(()=>{
if (typeof window !== `undefined`) {
new SimpleLightbox({elements: '.inspiration-grid a'})
}
},[])
It looks like the library is not loaded at the first initial render or the elements are not ready at the same time that the lightbox is requesting them, but when hydration occurs, everything works as it should, so loading it at the right time or forcing a reload should work.
This question already has answers here:
Passing in parameter to Styled Components
(3 answers)
Closed 18 days ago.
I'm practicing how to use the map function.
I made a json file and I want to show each data using map function.
Data is handed over well. But the problem is the way to show images.
Images are in one file and the way to show images is showing the part of image using background-position.
To do this, I put the taget 'background-position' figures to json file.
Qusetion
But I don't know how to deliever this. Tried to make code, but it doesn't work.
I think the method of delivering the value to the styled component is wrong. How can I show different images?
The problematic place is where the id is list__img.
You will understand the code easier if you see codespan.
code
import { useState } from "react";
import styled from "styled-components";
import dummy from "./database/DB.json";
export default function App() {
const [data, setData] = useState(dummy.products);
return (
<Wrap>
<div className="characterList">
<div className="word">
<h2 className="word__font">Like Pick!</h2>
</div>
<div className="list">
<ul className="list__ul">
{data.map((item) => {
return (
<div className="list__box">
<Card key={item.id} item={item} />
</div>
);
})}
</ul>
</div>
</div>
</Wrap>
);
}
// I think I need to deliver the value here
function Card({ item }) {
return (
<div className="list__wrap">
<div className="list__img" />
{item.nameKr}
</div>
);
}
const Wrap = styled.div`
border: 1px solid black;
position: relative;
top: calc(50vh - 100px);
width: 1200px;
display: inline-flex;
.characterList {
border: 1px solid red;
}
.word {
margin: 0 auto;
width: 1200px;
}
.word__font {
font-family: "SFCompactDisplay-Bold", sans-serif;
font-weight: bold;
font-size: 38px;
margin-bottom: 25px;
text-align: center;
}
.list {
border: 3px solid grey;
margin: 0 auto;
width: 1200px;
padding-bottom: 20px;
}
.list__ul {
display: inline-flex;
list-style: none;
}
.list__box {
margin: 0 9px;
text-align: center;
font-size: 17px;
}
.list__wrap {
color: #333;
font-size: 13px;
display: inline-block;
text-align: center;
}
.list__img {
background-image: url(https://www.kakaofriendsgolf.com/img/v3_img_sprites_friends#3x.png);
background-repeat: no-repeat;
width: 70px;
height: 70px;
background-size: 100%;
margin-bottom: 5px;
/* this part is the problem */
background-position: 0 {item.position}%;
}
`;
database
{
"products": [
{
"id": "1",
"name": "choosik",
"nameKr": "춘식이",
"position": 17.647059
},
{
"id": "2",
"name": "ryan",
"nameKr": "라이언",
"position": 88.235295
},
{
"id": "3",
"name": "apeach",
"nameKr": "어피치",
"position": 5.882353
}
]
}
codespan
https://codesandbox.io/s/characterselectmap-t2mqef?file=/src/App.js:0-1990
You can pass parameter into styled component.
But the way you did it, it wont work.
Separate 'list__img' into new Styled Component, then you can access those props.
Or you can style it inline
<div className="list__img" style={{backgroundPosition: `0 ${item.position}%`}} />
component Publisher.js and other child components projectStatus.js are overlapping each other when Render the Child component. I don't know what's going wrong and how to fix this. You can see the image
this is my Publisher.js
//import useState hook to create menu collapse state
import React, { useState } from "react";
import {NavLink, Outlet} from "react-router-dom"
//import react pro sidebar components
import {
ProSidebar,
Menu,
MenuItem,
SidebarHeader,
SidebarFooter,
SidebarContent,
} from "react-pro-sidebar";
//import icons from react icons
import { FaFileContract } from "react-icons/fa";
import { FiLogOut} from "react-icons/fi";
import { HiDocumentReport } from "react-icons/hi";
import { BiCog } from "react-icons/bi";
import { GiHamburgerMenu } from "react-icons/gi";
//import sidebar css from react-pro-sidebar module and our custom css
import "react-pro-sidebar/dist/css/styles.css";
import "./publisherCss.css";
const Publisher = () => {
//create initial menuCollapse state using useState hook
const [menuCollapse, setMenuCollapse] = useState(false)
//create a custom function that will change menucollapse state from false to true and true to false
const menuIconClick = () => {
//condition checking to change state from true to false and vice versa
menuCollapse ? setMenuCollapse(false) : setMenuCollapse(true);
};
return (
<>
<div id="sidebarHeader">
{/* collapsed props to change menu size using menucollapse state */}
<ProSidebar collapsed={menuCollapse}>
<SidebarHeader>
<div className="logotext">
{/* small and big change using menucollapse state */}
<p>{menuCollapse ? "Evc" : "Publisher "}</p>
</div>
<div className="closemenu" onClick={menuIconClick}>
{/* changing menu collapse icon on click */}
{menuCollapse ? (
<GiHamburgerMenu/>
) : (
<GiHamburgerMenu/>
)}
</div>
</SidebarHeader>
<SidebarContent>
<Menu iconShape="square">
<NavLink to="/publisher/projectstatus"> <MenuItem icon={<FaFileContract />}>Project status</MenuItem> </NavLink>
<MenuItem icon={<HiDocumentReport />}>All project</MenuItem>
<MenuItem icon={<BiCog />}>Settings</MenuItem>
</Menu>
</SidebarContent>
<SidebarFooter>
<NavLink to="/login">
<Menu iconShape="square">
<MenuItem icon={<FiLogOut />}>Logout</MenuItem>
</Menu>
</NavLink>
</SidebarFooter>
</ProSidebar>
</div>
<Outlet />
</>
)
}
export default Publisher;
Publisher.css
#sidebarHeader {
position: absolute;
width: 220px;
display: flex;
}
#sidebarHeader .pro-sidebar {
height: 100vh;
/* position: absolute; */
}
#sidebarHeader .closemenu {
color: rgb(0,7,61);
position: absolute;
right: 0;
z-index: 9999;
line-height: 20px;
border-radius: 50%;
font-weight: bold;
font-size: 22px;
top: 55px;
cursor: pointer;
}
#sidebarHeader .pro-sidebar {
width: 100%;
min-width: 100%;
}
#sidebarHeader .pro-sidebar.collapsed {
width: 80px;
min-width: 80px;
}
#sidebarHeader .pro-sidebar-inner {
background-color: white;
box-shadow: 0.5px 0.866px 2px 0px rgba(0, 0, 0, 0.15);
}
#sidebarHeader .pro-sidebar-inner .pro-sidebar-layout {
overflow-y: hidden;
}
#sidebarHeader .pro-sidebar-inner .pro-sidebar-layout .logotext p {
font-size: 20px;
padding: 10px 20px;
color: #000;
font-weight: bold;
}
#sidebarHeader .pro-sidebar-inner .pro-sidebar-layout ul {
padding: 0 5px;
}
#sidebarHeader .pro-sidebar-inner .pro-sidebar-layout ul .pro-inner-item {
color: #000;
margin: 10px 0px;
font-weight: bold;
}
#sidebarHeader .pro-sidebar-inner .pro-sidebar-layout ul .pro-inner-item .pro-icon-wrapper {
background-color: #fbf4cd;
color: #000;
border-radius: 3px;
}
#sidebarHeader .pro-sidebar-inner .pro-sidebar-layout ul .pro-inner-item .pro-icon-wrapper .pro-item-content {
color: #000;
}
#sidebarHeader .pro-sidebar-inner .pro-sidebar-layout .active {
background-image: linear-gradient(0deg, #fece00 0%, #ffe172 100%);
}
#sidebarHeader .logo {
padding: 20px;
}
#media only screen and (max-width: 720px) {
html {
overflow: hidden;
}
}
.nav-link .active{
background-color: #ffe172;
}
I think I am doing some wrong CSS override property but I am unable to understand what's wrong I am doing. if anyone know please correct me
if anyone knows how to fix this please tell me. it's appreciated
update:
After updating the CSS display: flex it show the child content in flex but the problem is, I specified width: 220px for the sidebar but the child content not go above the 220px width. you can see the image.
Now how can I fix this to a child can use width?
Your picture is not complete, most likely it's styles or wrong location of "Outlet". I made simplified example, I hope it helps.
I have one large image on the top and three smaller images on the bottom of my page. I want to make it so when you hover the small image (it's also a link which goes to different place) the large image changes to this small image. So clearly explained, two images change/swap places. And when I unhover the large image changes back. I tried a couple solution but it didn't work so I'm in trouble, can you please help me?
I'm looking for CSS/Javascript-solution NOT JQUERY!
Thanks and sorry for bothering!
import React from "react";
import { Link } from "react-router-dom";
import "../screens/StyleName.css";
import { Card } from "react-bootstrap";
import Categories from "../components/Categories.json";
import { useLocation } from "react-router-dom";
// images:
import LargeImage from "../images/largeimage.jpg;
import SmallImage1 from "../images/small_image1.jpg";
import SmallImage2 from "../images/small_image2.jpg";
import SmallImage3 from "../images/small_image3.jpg";
import Placeholder from "../images/placeholder.jpg";
function FunctionName() {
let location = useLocation();
const ShowImage = (ImageName) => {
if (ImageName === "SmallImage1") {
return SmallImage1;
} else if (ImageName === "SmallImage2") {
return SmallImage2;
} else if (ImageName === "SmallImage3") {
return SmallImage3;
} else {
return Placeholder;
}
};
const AllCategories = () => {
return Categories.map((category, index) => {
const data = () => {
if (category.children) {
return { data: category.children };
} else {
return { data: undefined };
}
};
return (
<div key={index} className="category-card">
<Card style={{ border: "none" }}>
<Link
to={{
pathname: location.pathname + "/" + category.name,
state: data(),
}}
className="category-link link"
>
<div className="img__wrap">
<Card.Img
variant="top"
alt="card-img-top"
className="small-img-down img-responsive img__img"
src={ShowImage(category.name)}
/>
<p className="img__description">{category.name}</p>
</div>
</Link>
</Card>
</div>
);
});
};
return (
<div className="maincategory-top">
<div className="category-container">
<div className="col-sm-8">
<Link to="/product" className="category-link link">
<Card.Img
alt="card-img-top"
className="big-img img-responsive"
src={LargeImage}
/>
</Link>
<div className="row">{AllCategories()}</div>
</div>
</div>
</div>
);
}
export default FunctionName;
CSS:
.big-img {
display: block;
margin-left: auto;
margin-right: auto;
margin-bottom: 3%;
overflow: auto;
width: 470px;
height: 300px;
}
.category-container {
padding-left: 8%;
padding-bottom: 10%;
margin-top: 10%;
}
.card-title-main {
font-size: 10px;
letter-spacing: 1.2px;
font-weight: normal;
text-transform: uppercase;
}
a.category-link:link {
color: black;
background-color: transparent;
text-decoration: none;
}
a.category-link:visited {
color: black;
background-color: transparent;
text-decoration: none;
}
a.category-link:hover {
color: black;
background-color: transparent;
text-decoration: none;
}
a.category-link:active {
color: black;
background-color: transparent;
text-decoration: none;
}
.category2 {
margin-top: 1%;
text-align: center;
}
.small-img-down {
width: 120px;
height: 80px;
margin-right: 13px;
margin-bottom: 13px;
display: inline-block;
}
.small-img-down:hover {
transform: scale(0.9);
}
.category-card {
display: block;
margin-left: auto;
margin-right: auto;
}
.img__wrap:hover .img__description {
visibility: visible;
opacity: 1;
padding: 20px;
}
.img__description {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
text-transform: uppercase;
background: rgba(245, 245, 245, 0.363);
color: black;
letter-spacing: 1.2px;
text-align: center;
visibility: hidden;
font-size: 16px;
opacity: 0;
transition: opacity 0.2s, visibility 0.2s;
}
() => {
const default_large = "image_one"
const [largeImage, setLargeImage] = useState(default_large);
return(
<div>
<img
src={image_one}
onMouseEnter={() => setLargeImage("image_one")}
onMouseOut={() => setLargeImage(default_large)}
className={largeImage === "image_one" ? "large" : "small"}
/>
<img
src={image_two}
onMouseEnter={() => setLargeImage("image_two")}
onMouseOut={() => setLargeImage(default_large)}
className={largeImage === "image_two" ? "large" : "small"}
/>
<img
src={image_three}
onMouseEnter={() => setLargeImage("image_three")}
onMouseOut={() => setLargeImage(default_large)}
className={largeImage === "image_three" ? "large" : "small"}
/>
</div>
)
}
In your css.
img.large{
width: 500px
}
img.small{
width: 100px
}
Explanation:
Keep track of what image is large in your component state. Whenever an image is hovered on, update the state accordingly. After the hover event, return the state to its default state. The image will have the "large" class while its being hovered on, and when no image is being hovered on, the default large image will be large.
Styled components cause build crash
Guys, I have a problem with styled components ;/.
I'm using create-react-app and everything works fine until I "npm run build" and deploy the app on my FTP.
I keep getting this weird error and have not found any solution online. My code is attached below. Should be plain and simple.
Any ideas?
import styled from 'styled-components';
import countriesJSON from './countries.json';
const Container = styled.div`
max-width: 1200px;
margin: 0 auto;
`;
const Wrapper = styled.div`
display: flex;
flex-direction: column;
height: 100vh;
justify-content: center;
align-items: center;
`;
const Button = styled.button`
background: whitesmoke;
border-radius: 3px;
height: 5vh;
width: 20vw;
border: 2px solid orange;
color: 'slategrey';
margin: 0 1em;
padding: 0.25em 1em;
&:hover {
background: whitesmoke;
transform: scale(1.2);
`;
const Text = styled.h1`
font-size: calc(2vw + 10px);
font-weight: bold;
text-align: center;
color: whitesmoke;
`;
const Paragraph = styled.p`
color: whitesmoke;
font-size: calc(1vw + 10px);
`;
const CountryWrapper = styled.div`
display: flex;
flex-direction: column;
align-items: center;
#media (min-width: 600px) {
flex-direction: row;
}
`;
const CountryDetailsWrapper = styled.div`
margin: 10px;
text-align: center;
`;
const CountryImgWrapper = styled.div`
img {
height: 40vh;
margin: 0 auto;
width: 90vw;
padding: 10px;
}
#media (min-width: 596px) {
img {
width: 30vw;
height: 30vh;
}
}
`;
class Homepage extends Component {
constructor(props) {
super(props);
this.state = {
randomCountry: '',
isCountryWrapperVisible: false,
countryFlag: '',
countryRegion: '',
countryCurrency: '',
countryCapital: '',
countryPopulation: '',
countryTimezone: ''
}};
generateRandomCity = () => {
let num = Math.floor(Math.random() * countriesJSON.length);
this.setState({
randomCountry: countriesJSON[num].name
});
};
componentDidUpdate() {
this.fetchCountryInfo();
}
fetchCountryInfo = () => {
fetch(`https://restcountries.eu/rest/v2/name/${this.state.randomCountry}`)
.then(res => res.json())
.then(response =>
this.setState({
countryFlag: response[0].flag,
countryCapital: response[0].capital,
countryRegion: response[0].region,
countryCurrency: response[0].currency,
countryPopulation: response[0].population,
isCountryWrapperVisible: true
})
);
};
render() {
return (
<Container>
<Wrapper>
<Text> Test Your Knowledge of Flags </Text>
<Button onClick={this.generateRandomCity}> HIT ME! </Button>
{this.state.isCountryWrapperVisible && (
<CountryWrapper>
<CountryImgWrapper>
<img src={this.state.countryFlag} alt='flag' className='img-fluid'/>
</CountryImgWrapper>
<CountryDetailsWrapper>
<Paragraph> Name: {this.state.randomCountry} </Paragraph>
<Paragraph> Capital: {this.state.countryCapital} </Paragraph>
<Paragraph> Region: {this.state.countryRegion} </Paragraph>
<Paragraph> Population: {this.state.countryPopulation} </Paragraph>
</CountryDetailsWrapper>
</CountryWrapper>
) }
</Wrapper>
</Container>
)
}
}
export default Homepage;```
It turns out the problem lied in global-style object within parent component I had to recode.