setState and changing style conditionally don't work together - reactjs

I got some issues with the following code:
//FIRST PAGE
import React from "react";
import './index.css'
import Boxx from "./Boxx";
export default function WindowZero (prop){
//
// FETCH COMING DATA
const fetchData = async () =>{
const data = await prop.data
return data
}
// FUNCTION FIRST PAGE
function RenderZero (){
return (<div className="Zero--">
<h1 className="Zero--h" > Welcome to Quizzly</h1>
<p className="Zero--p">click the start button to start</p>
<button className="Zero--start" onClick={()=>{ prop.setZero()}}>Quiz Me</button>
</div>)
}
// CONDITIONAL RENDERING
if(prop.zero){
return <RenderZero/>
}
else{
return <Boxx dataI={fetchData()}/>
}
}
This is the first page. It supposes to show the user a button when it was clicked, it renders the other page which shows a page of questions:
// THE OTHER PAGE
import React from "react";
import './index.css'
import {useState, useEffect} from 'react'
export default function Boxx (prop) {
// STATES & VARIABLES INITIALIZAITION
var sAnswersA = []
for(let i = 0; i<5; i++){
sAnswersA.push({id:i, value:'', correct:null})
}
var [selectedAnswers,setSelectedAnswers] = useState(sAnswersA)
var [truth,setTruth] = useState(true)
var [comingData, setComingData] = useState([])
// FETCHING FUNCTION::
let fetching = async()=>{
let x = await prop.dataI;
return x;
}
// USEEFFECT & FETCHE INSIDE::
useEffect(()=>{
fetching().then((result)=>{
var array = result.slice()
if(array[0].incorrect_answers.length != 4){
array.map(item=>{
item.incorrect_answers.push(item.correct_answer)
shuffle(item.incorrect_answers)
})}
for(let i = 0; i< array.length; i++){
array[i]= {...array[i], id:i}
}
setComingData(array)
})
},[])
// SHUFFLE FUNCTION::
function shuffle(array) {
let currentIndex = array.length, randomIndex ;
// While there remain elements to shuffle.
while (currentIndex != 0) {
// Pick a remaining element.
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--;
// And swap it with the current element.
[array[currentIndex], array[randomIndex]] = [
array[randomIndex], array[currentIndex]];
}
return array;
}
// EVENTLISTENERS::
const handler = (e,obj) =>{
var targetD = e.target
console.log(targetD)
console.log(obj)
obj.incorrect_answers.map(item=>{
if(item===targetD.innerText){
console.log('true')
let nodess = targetD.parentNode.childNodes;
nodess.forEach(i=>{
if(i.classList.contains('selected-')){
i.classList.remove('selected-')
}
})
targetD.classList.add('selected-')
}
})
}
// FUNCTION TO SUM ANSWERS::
const sumAnswers = (e,obj) =>{
// console.log(selectedAnswers[0].id)
setSelectedAnswers(prev=>{
return prev.map(it=>{
return obj.id === it.id ? {...it, value:e.target.innerText} : {...it}
})
})
}
// SHUFFLE DATA === Done
// ADD EVENT LISTENERS; STYLES; SUBMIT BTN; COLLECT ANSWERS; CHECK ANSWERS; SHOW RESULT;
//console area...
console.log(selectedAnswers)
// FUNCTION RUN = CREATE THE DOM ELEMENTS::
const Run = () => {
return comingData.map(item=>{
return(
<div key={Math.random()*9} className="questionBody--" id={item.id}>
<p className="qB--question">{item.question}</p>
<div className="answers--" id={item.id}>
<p className="qB--answer" id="0" onClick={(e)=>handler(e,item)}>{item.incorrect_answers[0]}</p>
<p className="qB--answer" id="1" onClick={(e)=>handler(e,item)}>{item.incorrect_answers[1]}</p>
<p className="qB--answer" id="2" onClick={(e)=>handler(e,item)}>{item.incorrect_answers[2]}</p>
<p className="qB--answer" id="3" onClick={(e)=>handler(e,item)}>{item.incorrect_answers[3]}</p>
</div>
</div>
)
})
}
return(<Run/>)
}
here is my App.js:
import './App.css';
import WindowZero from './windowZeroComponent';
import React, {useState,useEffect} from 'react';
function App() {
var [zero, setZero] = useState(true)
//
var [dataI, setDataI] = useState([])
//
useEffect(()=>{
fetcData()
},[])
const fetcData = async () =>{
const getData = await fetch('https://opentdb.com/api.php?amount=5&category=27&type=multiple')
const getDataJson = await getData.json()
setDataI(getDataJson.results)
}
return (
<div className='App--'>
hi
<WindowZero data = {dataI} zero={zero} setZero = {()=>setZero(prev=>!prev)}/>
</div>
);
}
export default App;
CSS file:
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
*{
box-sizing:border-box;
padding: 0 ;
margin: 0 ;
}
/* */
.Zero--{
background-image: linear-gradient(#37f,#3af);
height:100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 10% 5%;
}
.Zero--h{
text-transform: uppercase;
font-size: 28px;
font-weight: 500;
color: #f0f0ff;
padding: 12px;
}
.Zero--p{
font-size: 20px;
font-weight: 300;
padding: 8px;
color: #f0f0f0;
}
.Zero--start{
border: none;
padding: 10px 16px;
margin: 4px;
font-size: 22px;
font-weight: 700;
text-transform: uppercase;
border-radius: 16px;
background-color: #0ff;
color: #333333;
cursor: pointer;
}.Zero--start:hover{
transition:all ease-in-out 400ms;
transform: scale(1.11);
}
/* */
.questionBody--{
display: flex;
flex-direction: column;
background-color: transparent;
border-bottom: 1px solid #a0a0a0;
margin: 30px 0px;
}
.qB--question{
color: #007;
outline-color: #333333;
font-size: 26px;
font-weight: 600;
padding: 4px;
margin-left: 12px;
text-shadow: 0px 0px 1px #111;
}
.answers--{
display: flex;
flex-direction: row;
background-color: transparent;
}
.qB--answer{
padding: 0px 16px;
margin: 12px;
border-radius: 10px;
border:1px solid #999;
text-align: center;
/* color: #22f; */
font-size: 18px;
font-weight: 500;
text-shadow: 0px 0px 1px #111;
display: flex;
justify-content: center;
align-items: center;
}.qB--answer:hover{
transition: all ease-in 400ms;
background-color: #007;
color: #fff;
cursor: pointer;
}
/* */
.Main--{
padding: 25px 15%;
min-height: 100vh;
background-image: url('./background--.jpg');
background-repeat: no-repeat;
background-size: cover;
}
.submit--{
display: flex;
flex-direction: row;
align-items: baseline;
justify-content: center;
}
.submit--button{
padding: 2px 26px;
background-color: #007;
color: #fff;
font-size: 20px;
font-weight: 600;
border-radius: 12px;
margin: 0px 15px;
border: none;
}.submit--button:hover{
transition: all ease-in 400ms;
color: #007;
cursor:pointer;
background-color: #fff;
}
.conclusion--{
font-size:18px;
font-weight: 600;
}
.selected-{
background-color:#0ff;
color: #700;
}
My problem is that on the Other page (Boxx.js), specifically in //EVENENTLISTENERS => handler function: I'm trying to put sumAnswers(e,obj){which collects the clicked answers and add it to a state} inside of it, but when I do, the handler() stops adding the style to the clicked answer. I tried to put both of them inside of a function that runs after clicking on each answer but that didn't work too.
hope someone could help.
thx for reading, sorry for my English.

Related

why this component height is not full?

This is my cart component code. I am using styled components.
import React from "react";
import styled from "styled-components";
import { useSelector, useDispatch } from "react-redux";
import { remove } from "../../features/cartSlice";
const Cart = () => {
const pizzaProducts = useSelector((state) => state.cart);
const dispatch = useDispatch();
console.log(pizzaProducts);
const handleRemove = (id) => {
dispatch(remove(id));
};
return (
<CartContainer>
<CartHeading>Your cart</CartHeading>
<CartWrapper>
{pizzaProducts.map((pizzaProduct, index) => {
return (
<CartCard key={index}>
<CartImg src={pizzaProduct.img} alt="err" />
<CartInfo>
<CartTitle>{pizzaProduct.name}</CartTitle>
<CartDescription>{pizzaProduct.desc}</CartDescription>
<CartPrice>{pizzaProduct.price}</CartPrice>
<CartButton
id="btn"
onClick={() => handleRemove(pizzaProduct.id)}
>
Remove
</CartButton>
</CartInfo>
</CartCard>
);
})}
</CartWrapper>
</CartContainer>
);
};
const CartContainer = styled.div`
height: 100vh;
width: 100%;
min-width: 100vh;
min-height: 100%;
background: #000;
#media screen and (max-width: 400px) {
width: 100%;
}
`;
const CartHeading = styled.h2`
height: 40px;
padding: 30px 0;
text-align: center;
padding-top: 5rem;
margin-bottom: 2rem;
color: white;
font-size: clamp(2rem, 2.5vw, 3rem);
letter-spacing: 4px;
text-transform: uppercase;
`;
const CartWrapper = styled.div`
display: flex;
flex-wrap: wrap;
padding: 10px 0;
justify-content: center;
`;
const CartCard = styled.div`
margin: 0 3rem;
width: 400px;
min-width: 400px;
line-height: 2;
`;
const CartImg = styled.img`
height: 400px;
min-width: 400px;
box-shadow: 8px 8px #fdc500;
`;
const CartInfo = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 1.5rem 0;
`;
const CartTitle = styled.h2`
font-weight: 400;
font-size: 1.5rem;
color: white;
word-spacing: 1px;
letter-spacing: 3px;
`;
const CartDescription = styled.p`
margin-bottom: 1rem;
color: white;
margin: 20px 0;
font-size: 20px;
text-align: center;
word-spacing: 1px;
letter-spacing: 3px;
`;
const CartPrice = styled.p`
margin-bottom: 1rem;
font-size: 2rem;
color: white;
word-spacing: 1px;
letter-spacing: 3px;
`;
const CartButton = styled.button`
font-size: 1rem;
padding: 1rem 4rem;
border: none;
background: #e31837;
color: #fff;
transition: 0.2 ease-out;
&:hover {
background: #ffc500;
transition: 0.2s ease-out;
cursor: pointer;
color: #000;
}
`;
export default Cart;
I am expecting the full height 100%.

Renaming button.jsx makes entire react web unable to compile

i've been tasked with renaming a Button.jsx file for my react web app into 'v2_button.jsx' however when I do so it makes the entire web app unable to compile as there are many times where 'Button' is called but not 'v2_button'. My question is how do I make it so that I can update the name of the jsx file and update every instance where the original Button.jsx is called into 'v2_button'?
Button.jsx file:
import styled from 'styled-components';
const Button = styled.Button`
display: flex;
align-items: center;
justify-content: center;
height: 50px;
width: 300px;
background: ${(props) => {
let bg;
if (props.disabled) {
bg = props.theme.colors.disabled.background;
} else if (props.color === 'secondary') {
bg = props.theme.colors.primary.main;
} else if (props.color === 'danger') {
bg = props.theme.colors.danger.main;
} else {
bg = props.theme.colors.white;
}
return bg;
}};
font-family: ${(props) => props.theme.font.font3.new};
font-size: 18px;
font-weight: ${(props) => props.theme.fontWeight.heavy_900};
letter-spacing: 0.105em;
color: ${(props) => {
let fontColor;
if (props.color === 'secondary') {
fontColor = props.theme.colors.textPrimary.light;
} else {
fontColor = props.theme.colors.textPrimary.purple.dark;
}
return fontColor;
}};
padding: ${(props) => {
let padding;
if (props.size !== 's') {
padding = '0 39px';
}
return padding;
}};
border-radius: 40px;
border: ${(props) =>
props.disabled
? `1px solid ${props.theme.colors.disabled.border}`
: 'none'};
white-space: nowrap;
transition: background ${(props) => props.theme.animations.short} linear;
:hover {
background: ${(props) => {
let bg;
if (props.disabled) {
bg = props.theme.colors.disabled.background;
} else if (props.color === 'secondary') {
bg = props.theme.colors.primary.light;
} else if (props.color === 'danger') {
bg = props.theme.colors.danger.light;
} else {
bg = props.theme.colors.secondary.light;
}
return bg;
}};
cursor: pointer;
}
`;
export default Button;
Example where Button is called:
import styled from 'styled-components';
import Button from "../../Button";
export const Wrapper = styled.div`
display: flex;
flex-direction: column;
`;
export const Container = styled.div`
display: flex;
flex-direction: column;
`;
export const NameContainer = styled.div`
width: 100%;
display: flex;
align-content: space-between;
flex-direction: row;
font-style: normal;
font-weight: ${(props) => props.theme.fontWeight.med_500};
font-size: ${(props) => props.theme.fontSize.med_16};
`;
export const List = styled.div`
display: flex;
`;
export const FieldLabel = styled.label`
height: 36px;
color: ${(props) => props.theme.colors.white};
font-family: ${(props) => props.theme.font.font1.demi};
margin: 0 0 25px 0;
text-align: center;
font-style: normal;
font-weight: ${(props) => props.theme.fontWeight.heavy_800};
font-size: ${(props) => props.theme.fontSize.large_28};
line-height: 50px;
`;
export const Selected = styled.label`
width: 158px;
height: 40px;
margin: 0 10px 20px 10px;
padding-top: 10px;
padding-left: 20px;
font-size: ${(props) => props.theme.fontSize.med_20};
color: ${(props) => props.theme.colors.white};
font-family: ${(props) => props.theme.font.font1};
caret-color: ${(props) => props.theme.colors.textSecondary.main};
:focus {
outline: none;
}
:focus-within {
box-shadow: inset 0px 0px 2px 1px
${(props) => props.theme.colors.textSecondary.main};
}
::placeholder {
color: ${(props) => props.theme.colors.textPrimary.light};
}
background: ${(props) => props.theme.colors.primary.main};
border: ${(props) => props.theme.borders.textSecondary};
border-radius: 4px;
`;
export const AddFieldButton = styled(Button)`
margin: 30px;
font-size: ${(props) => props.theme.fontSize.med_20};
color: ${(props) => props.theme.colors.textSecondary.main};
background: none;
width: 1px;
text-align: center;
`;
Compiling error I receive after changing Button.jsx to v2_button.jsx:
https://imgur.com/a/kC0AdmG
The best thing is to renamed all the imports.
But a 'workaround' to renaming all the imports could be:
Keep the Button.jsx
Instead of exporting the default implementation, import the implementation from v2_button.jsx file and re-export it from the Button.jsx file.
Thus, every import of Button.tsx will actually refer to the implementation of v2_button.tsx file.
Example:
Button_v1.tsx
const Button_v1 = () => (
<>Button v1</>
);
export default Button_v1;
Button_v2.tsx
const Button_v2 = () => (
<>Button v2</>
);
export default Button_v2;
Since all other files reference Button_v1 and you don't want OR can't update the imports, you could do this:
Button_v1.tsx (after)
import Button_v2 from "./Button_v2"; // <-- ADDED THIS
const Button_v1 = () => (
<>Button v1</>
);
// export default Button_v1; // <-- COMMENTED THIS
export default Button_v2; // <-- ADDED THIS

react transition effect not work after async thunk

Although I have updated the todo status, the checkbox effect is not working correctly, as if no effect has been applied, what could be the reason for this? I don't think there is a problem with the api file, but the api request is taking a long time.I think it's because css doesn't render again, I can't think of anything else..
Thank you for helping
import React from "react";
import { useDispatch } from "react-redux";
import { toggleTodos } from "../redux/slice/thunkActions";
import styled from "styled-components";
const Content = styled.div`
color: #fff;
text-transform: capitalize;
`;
const Options = styled.div`
display: flex;
align-items: center;
justify-content: center;
gap: 2rem;
`;
const EditButton = styled.button`
cursor: pointer;
background-color: #ff6b81;
padding: 0.7rem 2rem;
color: #fff;
border-radius: 0.5rem;
font-weight: bold;
`;
const InputWrapper = styled.label`
position: relative;
`;
const Input = styled.input`
position: absolute;
left: -99999px;
top: -99999px;
&:checked + span {
background-color: #1890ff;
transition: 1s;
&:before {
left: calc(100% - 0.2rem);
transform: translateX(-100%);
}
}
`;
const Slider = styled.span`
display: flex;
width: 5rem;
height: 2.5rem;
cursor: pointer;
border-radius: 10rem;
background-color: #fcebb6;
transition: background-color 0.4s;
&:before {
content: "";
position: absolute;
top: 0.2rem;
left: 0.2rem;
width: 2.1rem;
height: 2.1rem;
border-radius: 2.1rem;
transition: 1s;
background-color: #fff;
}
`;
const Todo = ({ todo }) => {
const dispatch = useDispatch();
const handleChange = (todo) => {
dispatch(toggleTodos(todo));
};
return (
<li>
<Content>{todo.content}</Content>
<Options>
<EditButton type="button">Edit</EditButton>
<InputWrapper htmlFor={`todoContent${todo.id}`}>
<Input
id={`todoContent${todo.id}`}
type={"checkbox"}
onChange={() => handleChange(todo)}
checked={todo && todo.isCompleted}
/>
<Slider />
</InputWrapper>
</Options>
</li>
);
};
export default Todo;

In React, When I close the modal, I cannot scroll down/up

I am just getting started learning React with Function Component and Styled-Components. I created Modal Component and when I clicked the modal it shows up and I cannot scroll down/up(Background) but when I close the modal, I cannot scroll. I expect it should be work when I close the modal. Could you help me with what part something wrong?
const [showModal, setShowModal] = useState(false);
const [active, setActive] = useState(true);
const openModal = active => {
setShowModal(prev => !prev);
if (active) {
document.body.style.overflow = 'hidden';
} else {
document.body.style.overflow = 'auto';
}
};
<ShowAllSizes>
<AllSize>모든 사이즈</AllSize>
<FaRegArrowAltCircleDown // isActive={active} onClick={()=> openModal}
className="FaIconModal"
/>
<Modal showModal={showModal} setShowModal={setShowModal} />
</ShowAllSizes>
I will leave the whole code just in case
import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import styled from 'styled-components';
import { FaRegArrowAltCircleDown } from 'react-icons/fa';
import { Modal } from './components/Modal';
export default function ColumnTop() {
const [showModal, setShowModal] = useState(false);
const [active, setActive] = useState(true);
const openModal = active => {
setShowModal(prev => !prev);
if (active) {
document.body.style.overflow = 'hidden';
} else {
document.body.style.overflow = 'auto';
}
};
return (
<RightSideSection>
<ColumnTopSection>
<TitleBox>
<BrandName>Jordan</BrandName>
<EngName>Jordan 1 Retro High OG Bordeaux</EngName>
<KrName>조던 1 레트로 하이 OG 보르도</KrName>
</TitleBox>
<FigureWrap>
<DetailSize>
<SizeInKorean>사이즈</SizeInKorean>
<ShowAllSizes>
<AllSize>모든 사이즈</AllSize>
<FaRegArrowAltCircleDown
// isActive={active}
onClick={() => openModal}
className="FaIconModal"
/>
<Modal showModal={showModal} setShowModal={setShowModal} />
</ShowAllSizes>
</DetailSize>
<DetailPrice>
<TransactionPriceKorean>최근 거래가</TransactionPriceKorean>
<PriceInfo>
<CurrentPrice>248,000</CurrentPrice>
<PricePunctuation>6000원(-2.4%)</PricePunctuation>
</PriceInfo>
</DetailPrice>
<ButtonWrapper>
<Button>
<ButtonDivision>
<LeftSide>
<BtnTitle to="/order">구매</BtnTitle>
</LeftSide>
<RightSide>
<OrderPrice>23500원</OrderPrice>
<OrderStatusName>즉시 구매가</OrderStatusName>
</RightSide>
</ButtonDivision>
</Button>
<Button primary>
<ButtonDivision>
<LeftSide>
<BtnTitle to="/order">판매</BtnTitle>
</LeftSide>
<RightSide>
<OrderPrice>11500원</OrderPrice>
<OrderStatusName>즉시 판매가</OrderStatusName>
</RightSide>
</ButtonDivision>
</Button>
</ButtonWrapper>
</FigureWrap>
</ColumnTopSection>
</RightSideSection>
);
}
const ShowAllSizes = styled.div``;
const RightSideSection = styled.div`
padding-left: 40px;
`;
const ColumnTopSection = styled.div`
display: flex;
flex-direction: column;
`;
const TitleBox = styled.div`
display: flex;
flex-direction: column;
`;
const BrandName = styled.span`
display: inline-block;
vertical-align: top;
line-height: 19px;
padding-top: 1px;
margin-bottom: 9px;
font-size: 18px;
font-weight: 800;
border-bottom: 2px solid #222;
width: 61px;
height: 21px;
`;
const EngName = styled.span`
margin-bottom: 4px;
font-size: 18px;
font-weight: 400;
`;
const KrName = styled.span`
line-height: 17px;
font-size: 14px;
color: rgba(34, 34, 34, 0.5);
`;
const FigureWrap = styled.div`
width: 500px;
`;
const DetailSize = styled.div`
padding-top: 19px;
padding-bottom: 12px;
border-bottom: 1px solid #ebebeb;
`;
const SizeInKorean = styled.span`
padding-top: 4px;
display: inline-block;
line-height: 16px;
font-size: 13px;
color: rgba(34, 34, 34, 0.8);
`;
const AllSize = styled.span`
display: inline-block;
margin-right: 5px;
margin-left: 392px;
font-size: 18px;
`;
const FaIconModal = styled.button``;
const DetailPrice = styled.div`
display: flex;
margin-top: 4px;
`;
const TransactionPriceKorean = styled.span`
position: absolute;
width: 60px;
padding-top: 5px;
height: 7px;
font-size: 13px;
color: rgba(34, 34, 34, 0.8);
`;
const PriceInfo = styled.div`
display: flex;
flex-direction: column;
margin-left: 330px;
`;
const CurrentPrice = styled.span`
margin-left: 100px;
margin-top: 4px;
`;
const PricePunctuation = styled.span`
color: #31b46e;
margin-left: 110px;
`;
const ButtonWrapper = styled.div`
margin-top: 15px;
`;
const ButtonDivision = styled.div`
display: flex;
`;
const Button = styled.button`
background: ${props => (props.primary ? '#41B979' : '#EF6253')};
width: 235px;
height: 40px;
margin-right: 15px;
padding: 0 10px;
border: none;
border-radius: 5px;
`;
const LeftSide = styled.div`
margin-top: 3px;
left: 5px;
`;
const BtnTitle = styled(Link)`
font-weight: bold;
color: #fff;
text-decoration: none;
`;
const RightSide = styled.div`
margin-left: 10px;
border-left: 1px solid silver;
color: #fff;
`;
const OrderPrice = styled.div`
margin-left: 10px;
`;
const OrderStatusName = styled.div`
margin-left: 20px;
`;
This is my Modal Component
import React, { useRef, useState, useEffect, useCallback } from 'react';
import styled from 'styled-components';
import { MdClose } from 'react-icons/md';
export const Modal = ({ showModal, setShowModal }) => {
const modalRef = useRef();
const [detailPageData, setDetailPageData] = useState([]);
useEffect(() => {
fetch(`/data/detailPageData.json`)
.then(res => res.json())
.then(data => setDetailPageData(data.result.price_by_size));
}, []);
const closeModal = e => {
if (modalRef.current === e.target) {
setShowModal(false);
}
};
const keyPress = useCallback(
e => {
if (e.key === 'Escape' && showModal) {
setShowModal(false);
}
},
[setShowModal, showModal]
);
useEffect(() => {
document.addEventListener('keydown', keyPress);
return () => document.removeEventListener('keydown', keyPress);
}, [keyPress]);
// console.log(setDetailPageData);
return (
<div>
{showModal ? (
<Background onClick={closeModal} ref={modalRef}>
<ModalWrapper showModal={showModal}>
<SizeModalContent>
<Title>사이즈</Title>
<ButtonWrapper>
{detailPageData.map(({ id, title, price }) => {
return (
<Button key={id}>
<Size>{title}</Size>
<Price>{price}</Price>
</Button>
);
})}
</ButtonWrapper>
</SizeModalContent>
<CloseModalButton
aria-label="Close modal"
onClick={() => setShowModal(prev => !prev)}
/>
</ModalWrapper>
</Background>
) : null}
</div>
);
};
const Background = styled.div`
position: fixed;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
left: -0px;
top: -0px;
background: rgba(0, 0, 0, 0.8);
z-index: 10;
`;
const ModalWrapper = styled.div`
width: 400px;
height: 400px;
background: #fff;
color: #000;
border-radius: 10px;
`;
const SizeModalContent = styled.div`
margin-left: 20px;
margin-right: 20px;
`;
const Title = styled.div`
margin: 15px;
font-weight: 700;
color: #000;
text-align: center;
`;
const ButtonWrapper = styled.div`
margin: 10px 0px;
display: grid;
grid-template-columns: repeat(3, 33%);
`;
const Button = styled.button`
width: 100px;
height: 35px;
margin-left: 5px;
margin-bottom: 5px;
border: 1px solid #d3d3d3;
border-radius: 10px;
background-color: #fff;
`;
const Size = styled.span`
display: block;
font-weight: 700px;
`;
const Price = styled.span`
color: red;
`;
const CloseModalButton = styled(MdClose)`
cursor: pointer;
position: absolute;
top: 350px;
right: 800px;
font-size: 20px;
/* top: -50px;
right: -360px;
width: 32px;
height: 32px; */
`;
Edit1: Copy and Past my Modal.js

How to upload image with preview in react js

I have a reactjs application. I would like to know how to upload image with preview. Please advise.
you must read this mdn document about FileReader.
And then, based on your understand, you can apply like this.
export default () => {
const [preview, setPreview] = useState(null);
const fileHandler = evt => {
const f = evt.target.files[0];
if (f) {
const reader = new FileReader();
reader.onload = ({ target: { result } }) => {
setPreview(result);
};
reader.readAsDataURL(f); // you can read image file as DataURL like this.
} else {
setPreview(null);
}
};
return (
<div>
<input
type="file"
accept="image/*"
onChange={fileHandler}
/>
{ preview && <img src={preview} /> }
</div>);
}
The following illustrated code example is a part of my project.
As long as if you aren't following strict react pratices then this should work really well. You can change the styling as per your needs.
import React, { Component } from 'react';
import './App.css';
class App extends Component {
render() {
const imageHandler = () => {
const inpFile = document.getElementById('inpFile');
const previewContainer = document.getElementById('imagePreview');
const previewImage = previewContainer.querySelector('.image-preview__image');
const previewDefaultText = previewContainer.querySelector('.image-preview__default-text');
inpFile.addEventListener('change', function() {
const file = this.files[0];
if (file) {
const reader = new FileReader();
previewDefaultText.style.display = 'none';
previewImage.style.display = 'block';
reader.addEventListener('load', function() {
previewImage.setAttribute('src', this.result);
});
reader.readAsDataURL(file);
} else {
previewDefaultText.style.display = null;
previewImage.style.display = null;
previewImage.setAttribute('src', '');
}
});
};
return (
<div>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
<div className="login">
<div className="reg-container">
<div className="profile-pic">
Add your Profile Picture:
<div className="add-picture">
<div className="image-preview" id="imagePreview">
<div className="card-image" />
<img alt="" className="image-preview__image" />
<span className="image-preview__default-text" />
</div>
<input
onClick={imageHandler}
type="file"
name="inpFile"
id="inpFile"
accept="image/*"
/>
<div className="label-holder">
<label className="image-upload" for="inpFile">
<i className="material-icons">add_photo_alternate</i>
Choose your Photo
</label>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
}
export default App;
and obviously the styling file App.css
#import url('https://fonts.googleapis.com/css?family=Lato|Varela+Round&display=swap');
*{
margin: 0;
padding: 0;
}
.profile-pic {
font-family: 'Varela Round', sans-serif;
text-align: center;
padding: 1rem;
padding-top: 0;
padding-bottom: 0;
font-weight: bold;
font-size: 1.5rem;
}
/* ============================== */
.add-picture{
margin: auto;
font-size: 1rem;
display: flex;
text-align: center;
flex-direction: column;
justify-content: center;
align-items: center;
font-family: 'Lato', sans-serif;
}
.image-preview{
width: 120px;
min-height: 150px;
border: 5px solid black;
margin-top: 15px;
border-radius: 15px;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
color: #000;
outline: none;
z-index: ;
}
.image-preview__image{
display: none;
width: 100%;
height: 150px;
display:block;
object-fit: cover;
z-index: -11;
}
span{
/* background-image: url("../blank_user.webp"); */
width: 120px;
height: 150px;
background-size: cover;
background-position: center;
z-index: -9;
position: absolute;
border-radius: 15px;
outline: none;
}
#inpFile[type="file"]{
display:none;
margin-top: 20px;
}
.label-holder{
width: 200px;
height: 50px;
margin-top: 1rem;
}
.image-upload{
color: white;
background-color: black;
border: 1px solid #000;
display: inline-block;
padding: 6px 12px;
cursor: pointer;
font-family: 'Lato', sans-serif;
display: flex;
justify-content: center;
border-radius: 50px;
}
.description {
font-family: 'Varela Round', sans-serif;
text-align: center;
padding: 1rem;
padding-top: 0;
font-weight: bold;
font-size: 1.5rem;
}
.description textarea{
font-family: 'Lato', sans-serif;
font-size: 1rem;
background: none;
width: 350px;
height: 100px;
border: 2px solid black;
border-radius: 10px;
outline: none;
text-align: center;
}
#media screen and (max-width:550px){
.description textarea{
width: 260px;
}
}
Consider watching this Tutorial and other online article
you can also add the state management to the <input/>

Resources