Add animation to map components in React - reactjs

So i take my data from sanity then map them and then render them as components
my code is
function Projects() {
return <ProjectList projects={projectData} />;
}
export default Projects;
import ProjectItem from "./ProjectItem";
function ProjectList(props) {
return (
<ul>
{props.projects.map((project) => (
<ProjectItem
key={project.id}
id={project.id}
title={project.title}
description={project.desc}
/>
))}
</ul>
);
}
export default ProjectList;
Card,
Project,
ProjectTitle,
ProjectDesc,
ProjectUrl,
BtnWrap,
} from "./ProjecItemElements";
function ProjectItem(props) {
return (
<Project>
<Card>
<ProjectTitle>{props.title}</ProjectTitle>
<ProjectDesc>{props.description}</ProjectDesc>
<BtnWrap>
<ProjectUrl>Test</ProjectUrl>
</BtnWrap>
</Card>
</Project>
);
}
export default ProjectItem;
Card, Project, ProjectTitle, ProjectDesc, ProjectUrl, BtnWrap, are custom components so i can customize them using that
import styled from "styled-components";
export const Card = styled.div`
background-color: white;
border-radius: 6px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
`;
export const Project = styled.li`
margin: 1rem 0;
padding: 1rem;
`;
export const ProjectTitle = styled.h3`
text-align: center;
font-size: 1.25rem;
color: #2c292b;
`;
export const ProjectDesc = styled.p`
color: black;
`;
export const BtnWrap = styled.div`
text-align: right;
width: 100%;
padding: 0 1.5rem 1.5rem 0;
`;
export const ProjectUrl = styled.button`
text-decoration: none;
color: initial;
`;
what i want is to animate the components in the ul
{props.projects.map((project) => (
<ProjectItem
key={project.id}
id={project.id}
title={project.title}
description={project.desc}
/>
so they will come from the bottom to top with a delay when the page 'loads'
(its not really load because React)

Related

REACT + TypeScript Accordion transition doesn't works

Sup guys i have a problem, i would like to do an animated accordion, it should to have a transition animated when it opens and collapse, and in icon switching
I'm creating by myself an accordion in react + typescript but this transition doesnt works and idk why, code below:
This my index.tsx
import { useState } from "react";
import { AccordionButton, AccordionContent, Wrapper } from "./styles";
import { AccordionProps } from "./interfaces";
import { BsChevronDown, BsChevronUp } from "react-icons/bs";
export default function Accordion({ title, text }: AccordionProps) {
const [isOpen, setIsOpen] = useState(false);
const handleClick = () => {
setIsOpen(!isOpen);
returnIcon();
};
const returnIcon = () => {
return isOpen ? <BsChevronUp /> : <BsChevronDown />;
};
return (
<Wrapper>
<AccordionButton onClick={handleClick}>
{title} {returnIcon()}
</AccordionButton>
<AccordionContent isOpen={isOpen}>
<p>{text}</p>
</AccordionContent>
</Wrapper>
);
}
and this is my styled component below:
import styled from "styled-components";
import { AccordionContentProps } from "./interfaces";
export const AccordionButton = styled.button`
background-color: #606582;
color: #ffffff;
cursor: pointer;
padding: 18px;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 15px;
transition: 0.6s;
&:hover {
background-color: #60658295;
}
> svg {
float: right;
}
`;
export const AccordionContent = styled.div<AccordionContentProps>`
display: ${(props) => (props.isOpen === false ? "none" : "block")};
padding: 0 18px;
background-color: white;
overflow: hidden;
`;
export const Content = styled.div`
padding: 10px 0px;
flex-wrap: wrap;
max-width: 750px;
margin-right: auto;
margin-left: auto;
#media only screen and (max-width: 600px) {
max-width: 80%;
}
`;
export const Wrapper = styled.div`
padding: 10px 0px;
`;
I've tried to add this code but still doesnt working
-webkit-transition: all 0.4s ease-in;
-moz-transition: all 0.4s ease-in;
-o-transition: all 0.4s ease-in;
transition: all 0.4s ease-in;
Try to replace the return block with the following:
return (
<Wrapper>
{isOpen && <AccordionButton onClick={handleClick}>
{title} <BsChevronUp />
</AccordionButton>}
{!isOpen && <AccordionButton onClick={handleClick}>
{title} <BsChevronDown />
</AccordionButton>}
<AccordionContent isOpen={isOpen}>
<p>{text}</p>
</AccordionContent>
</Wrapper>)
and we can also delete the function returnIcon - we don't need it

Closing modal window by clicking on backdrop (ReactJS)

Could you please help me with one issue? I'd like to make a closing modal window by clicking on backdrop (using ReactJS). But in result window is closing even i click on modal window (at any position).
Here is my code:
import React from "react";
import { Fragment } from "react";
import ReactDOM from "react-dom";
import "./_Modal.scss";
const Backdrop = (props) => {
return (
<div className="backdrop" onClick={props.onClose}>
{props.children}
</div>
);
};
const ModalOverlay = (props) => {
return <div className="modal">{props.children}</div>;
};
const portalItem = document.getElementById("overlays");
const Modal = (props) => {
return (
<Fragment>
{ReactDOM.createPortal(
<Backdrop onClose={props.onClose}>
<ModalOverlay>{props.children}</ModalOverlay>
</Backdrop>,
portalItem
)}
</Fragment>
);
};
export default Modal;
And here is CSS:
.backdrop {
position: fixed;
top:0;
left: 0;
width: 100%;
height: 100vh;
z-index: 20;
background-color: rgba(0,0,0, 0.7);
display: flex;
justify-content: center;
align-items: flex-start;
overflow: auto;
padding-bottom: 15vh;
}
.modal {
position: relative;
max-width: 70%;
top: 5vh;
background-color: $darkgrey;
padding: 1rem;
border-radius: 1.5rem;
box-shadow: 0 1rem 1rem rgba(0,0,0, 0.25);
z-index: 30;
}
}
I'm just start to learning frontend, therefore do not judge strictly )
Just for understanding other people: after adding event.stopPropagation() in ModalOverlay everything works!
const ModalOverlay = (props) => {
return (
<div
className="modal"
onClick={(event) => {
event.stopPropagation();
}}
>
{props.children}
</div>
);
};

onClick not working on Draggable icon in react js?

I have a draggable icon. But onClick is not working on that icon.
import styled from "#emotion/styled"
import Draggable from 'react-draggable';
import { BsPlusCircle } from 'react-icons/bs';
const StartWrapper = styled.div`
position: absolute;
bottom: 74px;
display: block;
margin: 0 auto;
width: calc(100% - 24px);
font-family: Montserrat, sans-serif;
font-size: 14px;
font-style: normal;
font-weight: 500;
letter-spacing: 0em;
text-align: center;
color: rgba(91, 91, 91, 0.69);
border: 1px solid white;
height: 170px;
line-height: 170px;
`
export const App = ( ) => {
const show = () => {
console.log("Show");
}
return (
<StartWrapper onClick={()=> start()}>
<Draggable>
<BsPlusCircle onClick={() => show()} />
</Draggable>
</StartWrapper>
)
}
The start function works. But the show function does not work. Any help will be greatly appreciated.
Looks like you need to pass a ref to the StartWrapper and Draggable components
import styled from "#emotion/styled";
import { useRef } from "react";
import Draggable from "react-draggable";
import { BsPlusCircle } from "react-icons/bs";
const StartWrapper = styled.div`
position: absolute;
bottom: 74px;
display: block;
margin: 0 auto;
width: calc(100% - 24px);
font-family: Montserrat, sans-serif;
font-size: 14px;
font-style: normal;
font-weight: 500;
letter-spacing: 0em;
text-align: center;
color: rgba(91, 91, 91, 0.69);
border: 1px solid white;
height: 170px;
line-height: 170px;
`;
export const App = () => {
const ref = useRef(null); //ADD THIS
const show = () => {
console.log("Show");
};
return (
//Then pass refs to StartWrapper and Draggable
<StartWrapper ref={ref} onClick={() => console.log("Hello")}>
<Draggable ref={ref}>
<BsPlusCircle onClick={() => show()} />
</Draggable>
</StartWrapper>
);
};
This works for me in codesanbox
findDOMNode is deprecated in StrictMode. findDOMNode was passed an instance of DraggableCore which is inside StrictMode

React Context API returns undefined

I'm quite new to React, and i'm trying to make a ToDoList. I have a Modal with a submit button that when pressed should add a ToDoItem. But since i didn't want to prop drill my way through this i wanted to use the Context API. The Context API confuses me quite a bit, maybe i'm just a moron, but i have a hard time understanding why i have to make a hook and pass that as the value in the provider. I thought that in the ToDoContext that i already defined the default value as a empty array, so i just did it again.
In the console at line 62, which is my initial render it says that it's undefined, after the pressing the Add ToDo I get the same message.
App.jsx
import React, { useState } from "react";
import { render } from "react-dom";
import { ThemeProvider } from "emotion-theming";
import { defaultTheme } from "./theme";
import { Global, css } from "#emotion/core";
import Header from "./components/Header";
import ToDoList from "./components/ToDoList";
import AddBtn from "./components/AddBtn";
import ToDoContext from "./ToDoContext";
const App = () => {
const [toDoItems] = useState([]);
return (
<>
{/*Global styling*/}
<Global
styles={css`
* {
margin: 0;
padding: 0;
box-sizing: border-box;
list-style: none;
text-decoration: none;
}
`}
/>
{/*App render start from here*/}
<ThemeProvider theme={defaultTheme}>
<ToDoContext.Provider value={toDoItems}>
<Header />
<main>
<ToDoList />
<AddBtn />
</main>
</ToDoContext.Provider>
</ThemeProvider>
</>
);
};
render(<App />, document.getElementById("root"));
ToDoContext.jsx
import { createContext } from "react";
const ToDoContext = createContext([[], () => {}]);
export default ToDoContext;
AddBtn.jsx
import React, { useState, useContext } from "react";
import { css } from "emotion";
import Modal from "../Modal";
import ToDoContext from "../ToDoContext";
const BtnStyle = css`
position: fixed;
bottom: 0;
right: 0;
cursor: pointer;
display: block;
font-size: 7rem;
`;
const ModalDiv = css`
position: fixed;
left: 50%;
background-color: #e6e6e6;
width: 60%;
padding: 20px 20px 100px 20px;
display: flex;
flex-direction: column;
align-items: center;
max-width: 400px;
height: 50%;
transform: translate(-50%, -50%);
border-radius: 20px;
top: 50%;
`;
const textareaStyle = css`
resize: none;
width: 100%;
height: 200px;
font-size: 1.25rem;
padding: 5px 10px;
`;
const timeStyle = css`
font-size: 3rem;
display: block;
`;
const modalSubmit = css`
width: 100%;
font-size: 3rem;
cursor: pointer;
margin-top: auto;
`;
const Label = css`
font-size: 2rem;
text-align: center;
display: inline-block;
margin-bottom: 50px;
`;
const AddBtn = () => {
const [showModal, setShowModal] = useState(true);
const [time, setTime] = useState("01:00");
const [toDoItems, setToDoItems] = useContext(ToDoContext);
console.log(toDoItems);
return (
<>
<div className={BtnStyle} onClick={() => setShowModal(!showModal)}>
<ion-icon name="add-circle-outline"></ion-icon>
</div>
{showModal ? (
<Modal>
<div className={ModalDiv}>
<div>
<label className={Label} htmlFor="time">
Time
<input
className={timeStyle}
type="time"
name="time"
value={time}
onChange={(e) => setTime(e.target.value)}
/>
</label>
</div>
<label className={Label} htmlFor="desc">
Description
<textarea
className={textareaStyle}
name="desc"
placeholder={`Notify yourself this message in ${time}`}
></textarea>
</label>
<button
className={modalSubmit}
onClick={() => {
setToDoItems(
toDoItems.push({
time,
})
);
}}
>
Add ToDo
</button>
</div>
</Modal>
) : null}
</>
);
};
export default AddBtn;
There are few issues in your code to fix:
useState returns a value and a setter. With this line of code, const [toDoItems] = useState([]);, you are just passing an empty array to your context.
So do this:
const toDoItems = useState([]);
In your ToDoContext.js, just pass an empty array as argument (initial value)
const ToDoContext = createContext([]);
Working copy of your code is here. (see console logs)
Also, I noticed that you are pushing the todo in setTodoItems in AddBtn.js.
Don't do this:
onClick={() => {
setToDoItems(
toDoItems.push({
time
})
);
}}
Do this:
onClick={() => {
setToDoItems(
toDoItems.concat([
{
time
}
])
);
}}

React styled components with themeContext

I have the following React Header component in which I'm trying to get a theme value from the ThemeContext.Consumer and pass it into my NavListItem. It's currently working (sort of) but I'd like to only pass it once and have it applied globally. Instead of having to write theme={theme.typography.navigation} each time I use the styled component?
import React from "react"
import { Link } from "gatsby"
import PropTypes from "prop-types"
import styled from "styled-components"
import ThemeContext from "../context/ThemeContext"
const Main = styled.header`
height: 70px;
background-color: white;
position: fixed;
width: 100%;
left: 0;
`
const NavWrapper = styled.div`
position: relative;
`
const NavListLeft = styled.ul`
color: black;
position: absolute;
top: 25px;
left: 40px;
list-style: none;
margin: 0;
padding: 0;
`
const NavListItem = styled.li`
display: inline-block;
margin: 0 10px;
font-family: ${props => props.theme};
font-size: 16px;
`
const Header = ({ siteTitle }) => (
<ThemeContext.Consumer>
{theme => (
<Main>
<NavWrapper>
<NavListLeft>
<NavListItem theme={theme.typography.navigation}>
<Link style={{color: 'black'}}>Women</Link>
</NavListItem>
<NavListItem theme={theme.typography.navigation}>Men</NavListItem>
<NavListItem theme={theme.typography.navigation}>Designers</NavListItem>
<NavListItem theme={theme.typography.navigation}>Collection</NavListItem>
<NavListItem theme={theme.typography.navigation}>Sale</NavListItem>
</NavListLeft>
</NavWrapper>
</Main>
)}
</ThemeContext.Consumer>
)
Header.propTypes = {
siteTitle: PropTypes.string,
}
Header.defaultProps = {
siteTitle: ``,
}
export default Header
You could create a NavListItem that consumes your theme.
const ThemedNavListItem = styled.li`
display: inline-block;
margin: 0 10px;
font-family: ${props => props.theme.typography.navigation};
font-size: 16px;
`
const NavListItem = props => (
<ThemeContext.Consumer>
{theme => <ThemedNavListItem {...props} theme={theme} />}
</ThemeContext.Consumer>
);
const Header = ({ siteTitle }) => (
<Main>
<NavWrapper>
<NavListLeft>
<NavListItem>
<Link style={{color: 'black'}}>Women</Link>
</NavListItem>
<NavListItem>Men</NavListItem>
<NavListItem>Designers</NavListItem>
<NavListItem>Collection</NavListItem>
<NavListItem>Sale</NavListItem>
</NavListLeft>
</NavWrapper>
</Main>
);
A simpler solution using styled-components ThemeProvider.
A helper component for theming. Injects the theme into all styled
components anywhere beneath it in the component tree, via the context
API.
const NavListItem = styled.li`
display: inline-block;
margin: 0 10px;
font-family: ${props => props.theme.typography.navigation};
font-size: 16px;
`
const Header = ({ siteTitle }) => (
<ThemeProvider theme={theme}>
<Main>
<NavWrapper>
<NavListLeft>
<NavListItem>
<Link style={{color: 'black'}}>Women</Link>
</NavListItem>
<NavListItem>Men</NavListItem>
<NavListItem>Designers</NavListItem>
<NavListItem>Collection</NavListItem>
<NavListItem>Sale</NavListItem>
</NavListLeft>
</NavWrapper>
</Main>
</ThemeProvider>
);

Resources