pseudo styled component h2 not showing in html - reactjs

I'm currently trying to create an animation on an H2, using the pseudos ::before and after. But the ::before and ::after are not showing in my HTML.
What am I doing wrong here? Looking at styled components docs this should just work. I'm aware of the weird animation function. But this does not have any influence in the before after. I already completely removed it but it still doesn't render.
import React from 'react'
import styled, { keyframes, css } from 'styled-components'
import PropTypes from 'prop-types'
const Wrapper = styled.h2`
position: relative;
font-size: 1.5rem;
color: #ffffff;
font-weight: 600;
text-align: center;
text-transform: uppercase;
letter-spacing: 0.01em;
transform: scale3d(1,1,1);
opacity: 1;
&::before, &::after{
content: ${(props) => props.text};
position: absolute;
top: 0;
left: 0;
right: 0;
overflow: hidden;
background: #333333;
color: #ffffff;
clip: rect(0, 900px, 0, 0);
}
&::before {
left: 7px;
text-shadow: 1px 0 green;
animation: ${glitchEffect} 3s infinite linear alternate-reverse;
}
&::after {
left: 3px;
text-shadow: -1px 0 red;
animation: ${glitchEffect} 2s infinite linear alternate-reverse;
}
`
const glitchEffect = keyframes`
${setInterval(createAnimation, 200)}
`
function createAnimation(){
const single = `clip: rect(${(Math.floor(Math.random() * 100 + 1))}px, 9999px, ${(Math.floor(Math.random() * 100 + 1))}px, 0);`
return css`${single}`
}
export default function Glitch({ text }){
return (
<Wrapper text={text}>{text}</Wrapper>
)
}
Glitch.propTypes = {
text: PropTypes.string.isRequired
}

A few things:
content: ${(props) => props.text}; wouldn't work, you need to add double quotes around the text like so content: "${(props) => props.text}";
The second issue is setInterval(createAnimation, 200). This will return an integer (a handle to the interval that you've just created). This will be needed to clear the interval once you're done with the animation for example.
If what you want to do is to generate a some keyframes, then you need to call createAnimation manually like so
import React from "react";
import styled, { keyframes, css } from "styled-components";
const glitchEffect = keyframes`
from {
${createAnimation()}
}
to {
${createAnimation()}
}
`;
const Wrapper = styled.h2`
position: relative;
font-size: 1.5rem;
color: #ffffff;
font-weight: 600;
text-align: center;
text-transform: uppercase;
letter-spacing: 0.01em;
transform: scale3d(1, 1, 1);
opacity: 1;
> .before,
> .after {
position: absolute;
top: 0;
left: 0;
right: 0;
overflow: hidden;
background: #333333;
color: #ffffff;
clip: rect(0, 900px, 0, 0);
}
> .before {
left: 7px;
text-shadow: 1px 0 green;
animation: ${glitchEffect} 3s infinite linear alternate-reverse;
}
> .after {
left: 3px;
text-shadow: -1px 0 red;
animation: ${glitchEffect} 2s infinite linear alternate-reverse;
}
`;
function createAnimation() {
const single = `clip: rect(${Math.floor(
Math.random() * 100 + 1
)}px, 9999px, ${Math.floor(Math.random() * 100 + 1)}px, 0);`;
return css`
${single}
`;
}
export default function Glitch({ text }) {
return (
<Wrapper>
<div className="before">{text}</div>
{text}
<div className="after">{text}</div>
</Wrapper>
);
}
If you want to generate random animation, then you'll need to create the interval from Glitch
// First transform your "animation: ${glitchEffect} 3s infinite linear alternate-reverse;"
// into a "transition: text-shadow 500ms linear;"
// Since we're manually changing its value
import React, { useState, useEffect } from "react";
import styled from "styled-components";
import PropTypes from "prop-types";
const Wrapper = styled.h2`
position: relative;
font-size: 1.5rem;
color: #ffffff;
font-weight: 600;
text-align: center;
text-transform: uppercase;
letter-spacing: 0.01em;
transform: scale3d(1, 1, 1);
opacity: 1;
> .before,
> .after {
position: absolute;
top: 0;
left: 0;
right: 0;
overflow: hidden;
background: #333333;
color: #ffffff;
}
> .before {
left: 7px;
text-shadow: 1px 0 green;
transition: clip 300ms linear;
}
> .after {
left: 3px;
text-shadow: -1px 0 red;
transition: clip 200ms linear;
}
`;
function createAnimation() {
return {
clip: `rect(${Math.floor(Math.random() * 100 + 1)}px, 9999px, ${Math.floor(
Math.random() * 100 + 1
)}px, 0)`
};
}
export default function Glitch({ text }) {
const [glitchEffect, setGlitchEffect] = useState(createAnimation());
useEffect(() => {
const interval = setInterval(() => setGlitchEffect(createAnimation()), 500);
return () => {
clearInterval(interval);
};
}, []);
// now pass glitchEffect to a "style" prop to your pseudo elements
// See https://stackoverflow.com/a/28269950/3877913
return (
<Wrapper>
<div className="before" style={glitchEffect}>
{text}
</div>
{text}
<div className="after" style={glitchEffect}>
{text}
</div>
</Wrapper>
);
}

Related

Implementing animation when removing Toast

I have a working ToastList that enables me to click a button multiple times and generate a toast each time. On entry, I have an animation, but when I remove the toast, I do not get an animation. I am using Typescript and functional components.
My component is as follows:
import React, { useCallback, useEffect, useState } from 'react';
import * as Styled from './Toast.styled';
export interface ToastItem {
id: number;
title: string;
description: string;
backgroundColor: string;
}
export interface ToastProps {
toastList: ToastItem[];
setList: React.Dispatch<React.SetStateAction<ToastItem[]>>;
}
export default function Toast(props: ToastProps) {
const deleteToast = useCallback(
(id: number) => {
const toastListItem = props.toastList.filter((e) => e.id !== id);
props.setList(toastListItem);
},
[props.toastList, props.setList]
);
useEffect(() => {
const interval = setInterval(() => {
if (props.toastList.length) {
deleteToast(props.toastList[0].id);
}
}, 2000);
return () => {
clearInterval(interval);
};
}, [props.toastList, deleteToast]);
return (
<Styled.BottomRight>
{props.toastList.map((toast, i) => (
<Styled.Notification
key={i}
style={{ backgroundColor: toast.backgroundColor }}
>
<button onClick={() => deleteToast(toast.id)}>X</button>
<div>
<Styled.Title>{toast.title}</Styled.Title>
<Styled.Description>{toast.description}</Styled.Description>
</div>
</Styled.Notification>
))}
</Styled.BottomRight>
);
}
And my styling is done using styled-components and is as follows:
import styled, { keyframes } from 'styled-components';
export const Container = styled.div`
font-size: 14px;
position: fixed;
z-index: 10;
& button {
float: right;
background: none;
border: none;
color: #fff;
opacity: 0.8;
cursor: pointer;
}
`;
const toastEnter = keyframes`
from {
transform: translateX(100%);
}
to {
transform: translateX(0%);
}
}
`;
export const BottomRight = styled(Container)`
bottom: 2rem;
right: 1rem;
`;
export const Notification = styled.div`
width: 365px;
color: #fff;
padding: 15px 15px 10px 10px;
margin-bottom: 1rem;
border-radius: 4px;
box-shadow: 0 0 10px #999;
opacity: 0.9;
transition .1s ease;
animation: ${toastEnter} 0.5s;
&:hover {
box-shadow: 0 0 12px #fff;
opacity: 1;
}
`;
export const Title = styled.p`
font-weight: 700;
font-size: 16px;
text-align: left;
margin-bottom: 6px;
`;
export const Description = styled.p`
text-align: left;
`;
When I click a button, I just add an element to the state list, like:
toastProps = {
id: list.length + 1,
title: 'Success',
description: 'Sentence copied to clipboard!',
backgroundColor: '#5cb85c',
};
setList([...list, toastProps]);
My component is rendered like:
<Toast toastList={list} setList={setList}></Toast>
I would like to add animation when a toast exits, but do not know how. I have tried changing the style according to an additional prop I would send to the styled components, but this way all the toasts animate at the same time. My intuition is that I should use useRef(), but I am not sure how. Thanks in advance for any help you can provide.

Mapped buttons that have opacity of 0 are showing, and are overlapping when resizing the window

I've successfully mapped over text and have styled it with transition effects when going from one slide to the next as you can see here:
Following a similar concept with buttons isn't working. There should only be one button active per slide like you see here:
I want the buttons to have the same effect as the text, but I'm getting behaviors like you see here:
As you can see, there is no transition effect on the button when clicking to the second slide, and it also appears in a lower spot.
And lastly, when resizing the window, buttons are overlapping like you see here:
Don't know what to try next.
Here's the ImageSlider component:
import { useState } from "react";
import { SliderData } from "../data";
import { categories } from "../data";
import ShopNowButtonActive from "./ShopNowButtonActive";
import { IoIosArrowBack } from "react-icons/io";
import { IoIosArrowForward } from "react-icons/io";
import "./ImageSlider.css";
import ShopNowButton from "./ShopNowButton";
const ImageSlider = ({ slides }) => {
const [current, setCurrent] = useState(0);
const length = slides.length;
const nextSlide = () => {
setCurrent(current === length - 1 ? 0 : current + 1);
};
const prevSlide = () => {
setCurrent(current === 0 ? length - 1 : current - 1);
};
return (
<div className="slider">
<IoIosArrowBack className="left-arrow" onClick={prevSlide} />
{SliderData.map((slide, index) => (
<div key={slide.id}>
<img
src={slide.img}
alt=""
className={index === current ? "slide active" : "slide"}
/>
<div className="info-container">
<div className={index === current ? "title active" : "title"}>
{slide.title}
</div>
<div className={index === current ? "desc active" : "desc"}>
{slide.desc}
</div>
{categories.map((item, index) =>
index === current ? (
<ShopNowButtonActive item={item} />
) : (
<ShopNowButton item={item} />
)
)}
</div>
</div>
))}
<IoIosArrowForward className="right-arrow" onClick={nextSlide} />
</div>
);
};
export default ImageSlider;
The css file:
.slider {
height: 90vh;
margin-bottom: 0.5rem;
}
.left-arrow {
position: absolute;
top: 45%;
left: 32px;
font-size: 2rem;
cursor: pointer;
opacity: 0.5;
z-index: 1;
}
.slide.active {
opacity: 1;
width: 100%;
height: 88%;
object-fit: cover;
object-position: center;
-webkit-clip-path: polygon(100% 0, 100% 80%, 50% 100%, 0 80%, 0% 0%);
clip-path: polygon(100% 0, 100% 80%, 50% 100%, 0 80%, 0% 0%);
}
.slide {
opacity: 0;
transition: 500ms opacity ease-in-out;
width: 100%;
height: 88%;
object-fit: cover;
object-position: center;
position: absolute;
-webkit-clip-path: polygon(100% 0, 100% 80%, 50% 100%, 0 80%, 0% 0%);
clip-path: polygon(100% 0, 100% 80%, 50% 100%, 0 80%, 0% 0%);
}
.info-container {
display: flex;
flex-direction: column;
width: 40%;
height: 100%;
position: absolute;
top: 45%;
right: 30px;
}
.title.active {
opacity: 1;
transition-delay: 700ms;
font-size: 4rem;
}
.title {
opacity: 0;
transition: 200ms opacity ease-in-out;
font-size: 4rem;
}
.desc.active {
opacity: 1;
padding-top: 1.5em;
transition-delay: 700ms;
font-size: 1.25rem;
font-weight: 500;
letter-spacing: 3px;
}
.desc {
opacity: 0;
padding-top: 1.5em;
transition: 200ms opacity ease-in-out;
font-size: 1.25rem;
font-weight: 500;
letter-spacing: 3px;
}
.right-arrow {
position: absolute;
top: 45%;
right: 32px;
font-size: 2rem;
cursor: pointer;
opacity: 0.5;
z-index: 1;
}
The ShopNowButtonActive component:
import React from "react";
import styled from "styled-components/macro";
import { Link } from "react-router-dom";
const ButtonActive = styled.button`
opacity: 1;
padding: 0.5rem;
margin-top: 2.5rem;
width: 8rem;
font-size: 20px;
background-color: transparent;
cursor: pointer;
transition-delay: 700ms;
`;
const ShopNowButtonActive = ({ item }) => {
return (
<Link to={`/products/${item.cat}`}>
<ButtonActive>SHOP NOW</ButtonActive>
</Link>
);
};
export default ShopNowButtonActive;
And finally, the ShopNowButton component:
import React from "react";
import styled from "styled-components/macro";
import { Link } from "react-router-dom";
const Button = styled.button`
opacity: 0;
/* display: none; */
padding: 0.5rem;
margin-top: 2.5rem;
width: 8rem;
font-size: 20px;
background-color: transparent;
cursor: pointer;
transition: 200ms opacity ease-in-out;
`;
const ShopNowButton = ({ item }) => {
return (
<Link to={`/products/${item.cat}`}>
<Button>SHOP NOW</Button>
</Link>
);
};
export default ShopNowButton;
(Sorry for the use of both an external css file and styled components.)
Any suggestions?
I have recreated the above scenario using some static data. I have modified some of the css. It is working as expected. I also observed that the only major difference between ShopNowButton and ShopNowButtonActive was opacity property which hides the element. The reason you are observing 2 buttons because they were all there in the dom actually.(They were not hiding properly due to which we are observing more shop now buttons and every time we click on next icon the corresponding button is being displayed. Basically all the buttons are there on the page itself.)
Please find the sandbox url below.
https://codesandbox.io/s/stackoverflow-6u05e7?file=/src/ImageSlider/ImageSlider.js

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;

I am trying to render a image and as the errorCount increases show an different image everything works but image not displaying i had it but broke it

I am trying to figure out why the image will not display. I am trying to A different object everytime errorCount increases. Everything works but image will not display. I had it at one point but now I cannot figure out why it will not display. All the functionality works except displaying the image I want to display.
strike-image-data.js
import './images/hangmanScoreImg1.png';
import './images/hangmanScoreImg2.png';
import './images/hangmanScoreImg3.png';
import './images/hangmanScoreImg4.png';
import './images/hangmanScoreImg5.png';
import './images/hangmanScoreImg6.png';
import './images/hangmanScoreImg7.png';
const strikeImageData = [
{
id: 1,
image: "./images/hangmanScoreImg1.png" ,
name: 'start stage',
phrase: 'You have 6 more wrong guesses left',
},
{
id: 2,
image:'./images/hangmanScoreImg2.png',
name: 'strike one stage',
phrase:'You have 5 more wrong guesses left',
},
{
id: 3,
image:'./images/hangmanScoreImg3.png',
name:'strike two stage',
phrase:'You have 4 more wrong guesses left',
},
{
id: 4,
image:'./images/hangmanScoreImg4.png',
name:'strike three stage',
phrase:'You have 3 more wrong guesses left',
},
{
id: 5,
image:'./images/hangmanScoreImg5.png',
name:'strike four stage',
phrase:'You have 2 more wrong guesses left',
},
{
id: 6,
image:'./images/hangmanScoreImg6.png',
name:'strike five stage',
phrase:'You have 1 more wrong guess left',
},
{
id: 7,
image:'./images/hangmanScoreImg7.png',
name:'gameover stage',
phrase:'Game Over',
}
];
export default strikeImageData;
gameboard.js
import React, { useState, useEffect } from 'react';
import LetterGrid from './letter-grid';
import ButtonGrid from './button-grid';
import strikeImageData from './strike-image-data';
import './app.css';
// eslint-disable-next-line
export default function({secretWord, maxErrors, isShown}) {
const [guessedLetters, setGuessedLetters] = useState([]);
const [errorCount, setErrorCount] = useState(0);
const [strikeStages] = useState(strikeImageData);
const [index, setIndex] = React.useState(0);
const letterGuessedHandler = function(letter) {
let val = letter.toLowerCase();
if (secretWord.toLowerCase().indexOf(val) === -1) {
setErrorCount(errorCount + 1);
setIndex(index + 1);
}
setGuessedLetters(prev => [...prev, val]);
}
useEffect(() => {
const lastIndex = strikeStages.length - 1;
if(index < 0) {
setIndex(lastIndex);
}
if(index > lastIndex) {
setIndex(0);
}
}, [index, strikeStages]);
return (
<div className={isShown ? '' : 'hidden'}>
<section className='section'>
<div className='section-center'>
{strikeStages.map((strikeStage, strikeStageIndex) => {
const { id, image, name, phrase } = strikeStage;
let position = 'nextSlide';
if (strikeStageIndex === index) {
position = 'activeSlide';
}
if (strikeStageIndex === index - 1 || (index === 0 && strikeStageIndex === strikeStages.length - 1)) {
position = 'lastSlide';
}
return (
<article className={position} key={id} >
<img src={image} alt={name} />
<h4>{name}</h4>
<p className="text">{phrase}</p>
</article>
);
})}
</div>
</section>
<div>
Mistakes Left: {maxErrors - errorCount}
</div>
<LetterGrid
secretWord={secretWord}
guessedLetters={guessedLetters}
/>
<ButtonGrid
letterGuessed={letterGuessedHandler}
isShown={errorCount < maxErrors}
/>
</div>
)
}
app.js
import {useState} from 'react';
import './app.css';
import GameBoard from './game-board';
import WordSelect from './word-select';
export default function App() {
const [secretWord, setSecretWord] = useState('');
return (
<div className="app-container">
<h1>Welcome to Hangman</h1>
<p>Do you want to play the Hangman Game?</p>
<div>
<WordSelect
isShown={!secretWord}
wordSelected={val => setSecretWord(val)}
/>
<GameBoard
secretWord={secretWord}
maxErrors={6}
isShown={secretWord}
/>
</div>
</div>
);
}
app.css
/*Global Styles */
:root {
/* dark shades of primary color*/
--clr-primary-1: hsl(21, 91%, 17%);
--clr-primary-2: hsl(21, 84%, 25%);
--clr-primary-3: hsl(21, 81%, 29%);
--clr-primary-4: hsl(21, 77%, 34%);
--clr-primary-5: hsl(21, 62%, 45%);
--clr-primary-6: hsl(21, 57%, 50%);
--clr-primary-7: hsl(21, 65%, 59%);
--clr-primary-8: hsl(21, 80%, 74%);
--clr-primary-9: hsl(21, 94%, 87%);
--clr-primary-10: hsl(21, 100%, 94%);
/* darkest grey - used for headings */
--clr-grey-1: hsl(209, 61%, 16%);
--clr-grey-2: hsl(211, 39%, 23%);
--clr-grey-3: hsl(209, 34%, 30%);
--clr-grey-4: hsl(209, 28%, 39%);
/* grey used for paragraphs */
--clr-grey-5: hsl(210, 22%, 49%);
--clr-grey-6: hsl(209, 23%, 60%);
--clr-grey-7: hsl(211, 27%, 70%);
--clr-grey-8: hsl(210, 31%, 80%);
--clr-grey-9: hsl(212, 33%, 89%);
--clr-grey-10: hsl(210, 36%, 96%);
--clr-white: #fff;
--clr-red-dark: hsl(360, 67%, 44%);
--clr-red-light: hsl(360, 71%, 66%);
--clr-green-dark: hsl(125, 67%, 44%);
--clr-green-light: hsl(125, 71%, 66%);
--clr-black: #222;
--transition: all 0.3s linear;
--spacing: 0.1rem;
--radius: 0.25rem;
--light-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
--dark-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
--max-width: 1170px;
--fixed-width: 620px;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,
Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
background: var(--clr-grey-10);
color: var(--clr-grey-1);
line-height: 1.5;
font-size: 0.875rem;
}
ul {
list-style-type: none;
}
img {
min-width: 150px;
min-height: 150px;
}
a {
text-decoration: none;
}
h1,
h2,
h3,
h4 {
letter-spacing: var(--spacing);
text-transform: capitalize;
line-height: 1.25;
margin-bottom: 0.75rem;
}
h1 {
font-size: 3rem;
}
h2 {
font-size: 2rem;
}
h3 {
font-size: 1.25rem;
}
h4 {
font-size: 0.875rem;
}
p {
margin-bottom: 1.25rem;
color: var(--clr-grey-5);
}
#media screen and (min-width: 800px) {
h1 {
font-size: 3rem;
}
h2 {
font-size: 2.5rem;
}
h3 {
font-size: 1.75rem;
}
h4 {
font-size: 1rem;
}
body {
font-size: 1rem;
}
h1,
h2,
h3,
h4 {
line-height: 1;
}
}
.app-container {
width: 550px;
}
.flex {
display: flex;
}
.flex-wrap {
flex-wrap: wrap;
}
.mt-10 {
margin-top: 10px;
}
.letter {
border: 1px solid gray;
font-size: 24px;
font-weight: bold;
line-height: 34px;
height: 34px;
width: 34px;
text-align: center;
}
.button {
font-size: 24px;
font-weight: bold;
line-height: 34px;
height: 34px;
width: 34px;
text-align: center;
margin-right: 4px;
margin-bottom: 4px;
}
.guessed {
display: none;
}
.hidden {
display: none;
}
/* Global Classes */
/* section */
.section {
width: 90vw;
margin: 5rem auto;
max-width: var(--max-width);
}
#media screen and (min-width: 992px) {
.section {
width: 95vw;
}
}
.section-center {
margin: 0 auto;
margin-top: 4rem;
width: 80vw;
height: 450px;
max-width: 800px;
text-align: center;
position: relative;
display: flex;
overflow: hidden ;
}
article {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
transition: var(--transition);
}
article.activeSlide {
opacity: 1;
transform: translateX(0);
}
article.lastSlide {
transform: translateX(-100%);
}
article.nextSlide {
transform: translateX(100%);
}

setState and changing style conditionally don't work together

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.

Resources