React CSS transition not working - reactjs

I have a tic tac toe board, and want to add a transition an element on a click.
For demo, I have a board with some cells filled. On click on the board, a new cell is filled. I wanted this new cell to come with a transition.
I have followed the React Transition Group doc. On click on any cell of the board, a circle appears on 2rd row 3rd col. This should appear with a Fade-in transition. I have added CSSTransition group, but transition is not happening. Something is amiss.
Following is the codesandbox:
Some sample code from the snippet
tic-tac-toe-board.js
import React from "react";
import "./tictactoe.css";
import { CSSTransition } from 'react-transition-group';
const Fade = ({ children, ...props }) => (
<CSSTransition {...props} timeout={1000} classNames="fade">
{children}
</CSSTransition>
);
class TicTacToeBoard extends React.Component {
constructor(props) {
super(props);
this.state = {
board: [
[0, 1, 0],
[2, 0, 0],
[0, 0, 0]
]
};
}
handleBoardClick = () => {
let newBoard = [
[...this.state.board[0]],
[...this.state.board[1]],
[...this.state.board[2]]
];
newBoard[1][2] = 1;
this.setState({board: newBoard});
}
render() {
return (
<div className="board" onClick={this.handleBoardClick}>
{this.state.board.map((row, rowIndex) =>
row.map((cell, colIndex) => {
return (
<div className="cell" key={`${rowIndex}-${colIndex}`}>
{cell === 1 && (
<Fade in={true}>
<img
src="https://image.ibb.co/nDDDuw/circle_outline.png"
alt=""
className="cell-content"
/>
</Fade>
)}
{cell === 2 && (
<Fade in={true}>
<img
src="https://image.ibb.co/jY0nMb/close.png"
alt=""
className="cell-content"
/>
</Fade>
)}
</div>
);
})
)}
</div>
);
}
}
export default TicTacToeBoard;
tictactoe.css
* {
box-sizing: border-box;
}
.board {
width: 90vmin;
height: 90vmin;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
margin-left: auto;
margin-right: auto;
box-shadow: 5px 5px 10px #aaa;
}
.cell {
width: 30vmin;
height: 30vmin;
border: 1px solid lightgrey;
}
.cell-content {
width: 80%;
height: 80%;
margin: 10%;
}
.fade-enter {
opacity: 0.01;
}
.fade-enter.fade-enter-active {
opacity: 1;
transition: opacity 1000ms ease-in;
}

I had a similar issue but solved when I enclosed the CSSTransition inside the TransitionGroup
const Fade = ({ children, ...props }) => (
<TransitionGroup>
<CSSTransition {...props} timeout={1000} classNames="fade">
{children}
</CSSTransition>
</TransitionGroup>);
And update the style something like this
.fade-enter {
opacity: 0.01;
}
.fade-enter-active {
opacity: 1;
transition: opacity 1000ms ease-in;
}
.fade-exit {
opacity: 1;
}
.fade-exit-active {
opacity: 0.01;
transition: opacity 1s ease-out;
}

Related

Why does my overlay not transition while rerendering?

I'm trying to transition a simple overlay if one of four thumbnails is clicked. My idea was to toggle the overlays opacity within the inline style of the element like this..
<div className='product-thumbnail-overlay' style={selectedThumbnail === id ? { opacity: 1 } : { opacity: 0 }}></div>
This is working until I wrap this code in a react function component to avoid repetitions of my code.
HTML
import { useState, useEffect } from 'react';
import './Product.scss';
import ProductImage1 from '../assets/images/image-product-1.jpg';
import ProductImage2 from '../assets/images/image-product-2.jpg';
import ProductImage3 from '../assets/images/image-product-3.jpg';
import ProductImage4 from '../assets/images/image-product-4.jpg';
import ProductThumbnail1 from '../assets/images/image-product-1-thumbnail.jpg';
import ProductThumbnail2 from '../assets/images/image-product-2-thumbnail.jpg';
import ProductThumbnail3 from '../assets/images/image-product-3-thumbnail.jpg';
import ProductThumbnail4 from '../assets/images/image-product-4-thumbnail.jpg';
const Product = () => {
const [selectedThumbnail, setSelectedThumbnail] = useState(1);
const getProductThumbnailById = (id) => {
// get the imported product thumbnail
};
const ProductThumbnail = ({ id }) => {
return (
<div className='product-thumbnail-container' onClick={() => setSelectedThumbnail(id)}>
<img src={getProductThumbnailById(id)} alt='thumbnail of product' className='product-thumbnail' />
<div
className='product-thumbnail-overlay'
style={selectedThumbnail === id ? { opacity: 1 } : { opacity: 0 }}
></div>
</div>
);
};
return (
<main>
<div className='product-images'>
<img src={ProductImage1} alt='product' className='product-image' />
<div className='product-thumbnails'>
<ProductThumbnail id={1} />
<ProductThumbnail id={2} />
<ProductThumbnail id={3} />
<ProductThumbnail id={4} />
</div>
</div>
<div className='product-details'></div>
</main>
);
};
export default Product;
important SCSS
.product-thumbnail-container {
position: relative;
flex: 20.5%;
cursor: pointer;
.product-thumbnail {
display: block;
max-width: 100%;
border-radius: variables.$br;
}
.product-thumbnail-overlay {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: rgba(255, 255, 255, 0.6);
border: 2px solid variables.$clr_prim_orange;
border-radius: variables.$br;
transition: all 2000ms ease-in-out;
}
}
Codepen with what im trying to accomplish (but with repetitions of code): https://codepen.io/Sandhexer/pen/JjLrZOQ?editors=0110
Codepen with current code: https://codepen.io/Sandhexer/pen/gOeGKpP?editors=0110
I think that the rerendering causes the element not to transition because the opacity doesnt really change. Is this right and does anyone know a solution or good workaround for this problem?

How do I use React's CSSTransitionGroup to fade-in/out my alert message?

I'm trying to build a React 16.13.0 Flash component, that I would like to fade-in and fade-out an alert message (for instance, to tell the user something has saved successfully). I'm using the CSSTransitionGroup to try and do this. I built this Flash component
import React, { useEffect, useState } from "react";
import Bus from "../Utils/Bus";
import { CSSTransition } from "react-transition-group";
import "./index.css";
export const Flash = () => {
let [visibility, setVisibility] = useState(false);
let [message, setMessage] = useState("");
let [type, setType] = useState("");
useEffect(() => {
Bus.addListener("flash", ({ message, type }) => {
setVisibility(true);
setMessage(message);
setType(type);
setTimeout(() => {
setVisibility(false);
}, 4000);
});
}, []);
useEffect(() => {
if (document.querySelector(".close") !== null) {
document
.querySelector(".close")
.addEventListener("click", () => setVisibility(false));
}
});
return (
visibility && (
<CSSTransition in={visibility} timeout={300} classNames="sample">
<div className={`alert alert-${type}`}>
<span className="close">
<strong>X</strong>
</span>
<p>{message}</p>
</div>
</CSSTransition>
)
);
};
and am using the following CSS ...
.alert {
color: white;
border-radius: 10px;
position: absolute;
top: 50px;
padding: 20px;
width: 100%;
display: flex;
align-items: center;
z-index: 1111;
}
.alert p {
margin: 0;
}
.alert-error {
background: lightcoral;
}
.alert-success {
background: lightgreen;
}
.close {
color: white;
cursor: pointer;
margin-right: 10px;
}
.sample-enter {
opacity: 0;
}
.sample-enter-active {
opacity: 0.5;
transition: opacity 300ms;
}
.sample-enter-done {
opacity: 1;
}
.sample-exit {
opacity: 1;
}
.sample-exit-active {
opacity: 0.5;
transition: opacity 300ms;
}
.sample-exit-done {
opacity: 0;
}
However, the message appears without a fade-in and then disappears without a fade-out. I'm not sure what else I'm doing wrong or need to add.
I'm not sure if the key is at the -appear css selector but the following one works on my component (styled with class fade)
.fade-appear {
opacity: 0;
z-index: 1;
}
.fade-appear.fade-appear-active {
opacity: 1;
transition: opacity 1000ms linear;
}
.fade-enter {
opacity: 0;
z-index: 1;
}
.fade-enter.fade-enter-active {
opacity: 1;
transition: opacity 5000ms linear 5000ms;
}
.fade-exit {
opacity: 1;
}
.fade-exit.fade-exit-active {
opacity: 0;
transition: opacity 5000ms linear;
}
.fade-exit-done {
opacity: 0;
}
using version 4.3.0
My exact component is this
<CSSTransition in={true}
appear={true}
timeout={500}
classNames="fade"
unmountOnExit>
Remove visibility && from your return statement:
return (
<CSSTransition in={visibility} timeout={300} classNames="sample">
<div className={`alert alert-${type}`}>
<span className="close">
<strong>X</strong>
</span>
<p>{message}</p>
</div>
</CSSTransition>
)
The in prop controls the enter / exit transition. Exit transition is triggered only when in is false. There is no scenario in your code when this happens.

Transitions with React - should I use TransitionGroup?

I want page contents in my application to transition smoothly. I have been attempting to do this using react-transition-group but I have struggled to achieve the correct implementation. The following link was informative:
https://coursework.vschool.io/react-transitions-with-react-transition-group/
It shows how to make modularize and use TransitionGroup (although not both at the same time, unfortunately).
I created a demo project (based on the above link) to troubleshoot this issue. I have two items in an array ‘contactComponents’. All I am trying to do at the moment is make this information appear and disappear using the show/hide button.
Here is the main body of the code:
const contactDetails = ['Gryffindor Tower, Hogwarts','Gryffindor Tower, Hogwarts'];
const contacts = ['Harry', 'Ron'];
export default class App extends React.Component {
constructor(props){
super(props);
this.state = {
count: 0,
showMyContact: false
};
this.showContact = this.showContact.bind(this);
}
showContact() {
this.setState({showMyContact: !this.state.showMyContact})
}
render() {
const styles = {
container: { display: 'flex', justifyContent: 'center', width: '100vw', height: 100, flexDirection: 'column', padding: 100 },
btn: { width: '100%', display: 'flex', justifyContent: 'center'},
h1: { border: '2px solid blue', padding: 5, display: 'flex'}
};
let contactComponents = [contacts[this.state.count], contactDetails[this.state.count]];
console.log(this.state.showMyContact)
return (
<div>
<div style={ styles.container }>
<TransitionGroup component={null}>
{ contactComponents.map((item, key) =>
<CSSTransition
in={this.state.showMyContact}
key={key}
timeout={800}
classNames={"fade"}>
<h1 style={styles.h1}>
{
item
}
</h1>
</CSSTransition>
)}
</TransitionGroup>
<div style={ styles.btn }>
<button onClick={ this.showContact }>show/hide</button>
</div>
</div>
</div>
)
}
}
scss file:
.fade-appear,
.fade-enter {
opacity: 0;
z-index: 1;
}
.fade-appear-active,
.fade-enter.fade-enter-active {
opacity: 1;
transition: opacity 600ms linear 200ms;
}
.fade-exit {
opacity: 1;
}
.fade-exit.fade-exit-active {
opacity: 0;
transition: opacity 200ms linear;
}
Currently, the contents appears even though showMyContact is false when the render function first calls. Changing the state of showMyContact with the show/hide button has no effect. The content does not fade in and out as expected.
This post:
page transitions without React-Router
suggests it might be better to use pure css to carry out transitions rather than react-transition-group. Am I just barking up the wrong tree?
I found out that using pure css transitions provides the desired solution. I do not know if a solution using TransitionGroup and CSSTransition is feasible but it doesn't look like it.
By changing the contents of the render function to:
render() {
let contactComponents = [contacts[this.state.count], contactDetails[this.state.count]];
let cssList = [
"List",
this.state.showMyContact ? "ListShow" : "ListHide"
];
console.log(this.state.showMyContact);
return (
<div>
<div className={"container"}>
<List show={cssList.join(' ')} myContent={contactComponents}/>
<div className={"btn"}>
<button onClick={ this.showContact }>show/hide</button>
</div>
</div>
</div>
)
}
...and adding the following const:
const List = (props) => {
return (
<div className={props.show}>
<h1 className={"h1"}> { props.myContent[0] } </h1>
<h1 className={"h1"}> { props.myContent[1] } </h1>
</div>
)};
...and importing the following css file:
.container {
display: flex;
justify-content: center;
width: 500px;
height: 100px;
flex-direction: column;
padding: 100px;
}
.h1 {
border: 2px solid blue;
padding: 5px;
display: flex;
}
.btn {
width: 100%;
display: flex;
justify-content: center;
}
.List {
display: flex;
flex-direction: column;
transition: all 0.4s ease-out;
}
.ListShow {
opacity: 1;
}
.ListHide {
opacity: 0;
}
...I can get the desired behaviour.

React-modal hides behind elements

I am trying to make use of react-modal for the first time. When I click on the sign-in button, the react-modal component is invoke but seems to be hiding behind the cover page which is a video landing page.
The React devtool displays the appropriate states before the sign-in button is clicked
before the sign-in button is clicked
When the sign-in button is now clicked, the react devtool now displays that the ModalPortal component is rendered showing the appropriate states
when the sign-in button is clicked
SignInModal.scss
.ReactModalPortal>div {
opacity: 0;
}
.ReactModalPortal .ReactModal__Overlay {
align-items: center;
display: flex;
justify-content: center;
transition: opacity 200ms ease-in-out;
}
.ReactModalPortal .ReactModal__Overlay--after-open {
opacity: 1;
}
.ReactModalPortal .ReactModal__Overlay--before-close {
opacity: 0;
}
.modal {
position: relative;
background: #464b5e;
color: white;
max-width: 90rem;
outline: none;
padding: 3.2rem;
text-align: center;
}
.modal__title {
margin: 0 0 1.6rem 0;
}
.modal__body {
font-size: 2rem;
font-weight: 300;
margin: 0 0 3.2rem 0;
word-break: break-all;
}
CoverPage.js Component
import Header from './Header';
import HeaderVideo from './HeaderVideo';
import SignInModal from './SignInModal';
import React, { Component } from 'react';
class CoverPage extends Component {
state = {
modalIsOpen: false
};
onOpenModal = () => {
this.setState(() => ({
modalIsOpen: true
}));
};
onCloseModal = () => {
this.setState(() => ({
modalIsOpen: false
}));
};
render() {
return (
<div>
<Header />
<HeaderVideo onOpenModal={this.onOpenModal} />
<SignInModal
modalIsOpen={this.state.modalIsOpen}
onOpenModal={this.onOpenModal}
onCloseModal={this.onCloseModal}
/>
</div>
);
}
}
export default CoverPage;
HeaderVideo.js Component
import React from 'react';
import Signup from './Signup';
import CoverInfo from './CoverInfo';
const HeaderVideo = props => {
return (
<div className="video-container">
<video preload="true" autoPlay loop volume="0" postoer="/images/1.jpg">
<source src="images/vine.mp4" type="video/mp4" />
<source src="images/vine1.webm" type="video/webm" />
</video>
<div className="video-content">
<div className="container content">
<div className="row">
<div className="col-md-9">
<CoverInfo onOpenModal={props.onOpenModal} />
</div>
<div className="col-md-3">
<Signup />
</div>
</div>
</div>
</div>
</div>
);
};
export default HeaderVideo;
CoverInfo.js Component
import React from 'react';
const CoverInfo = props => {
return (
<div className="info">
<div>
<h1>Welcome to EventCity!</h1>
</div>
<div>
<p>
At EventCity! we pride ourselves on the unrivalled personal {`event`} services,we provide
to our clientele. We guide you from the stressful decision making {`process`},ensuring you
are comfortable,whether it is a wedding, corporate {`function `}or even a kiddies party,we
create a buzz around you, taking you to the next level.
</p>
</div>
<div>
<h3>Innovation, {`Performance`} and Delivery</h3>
</div>
<button type="button" className="btn btn-success btn-lg" onClick={props.onOpenModal}>
Sign In here
</button>
</div>
);
};
export default CoverInfo;
video-cover.scss
video {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
min-width: 100%;
min-height: 100%;
width: auto;
height: auto;
z-index: 1;
}
.video-content {
z-index: 2;
position: absolute;
background: rgba(0, 0, 0, 0.6);
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.content {
padding-top: 120px;
}
You need to set the z-index property on the Modal's overlay, which normally has a z-index of 0. The CSS class is .ReactModal__Overlay
Here is the pure-React way of doing it:
const customStyles = {
content : {
...
},
overlay: {zIndex: 1000}
};
<Modal style={customStyles}>
...
</Modal>
.modal {
position: fixed;
z-index:9999;
top :0;
left:0;
right:0;
bottom:0;
background: #464b5e;
color: white;
outline: none;
padding: 3.2rem;
text-align: center;
}
Example of react-modal inline styles Set the styles in the react-modal inline styles. The z-index to 100 but make just like below
style={{
overlay: {
zIndex: 100,
backgroundColor: 'rgba(70, 70, 70, 0.5)',
},

React CSS Transitions

I'm learning React CSS Transitions. So I decided to make a sliding sidebar navigation. The sidebar slides in from right just fine. But I can't get leave animations working. I'm not sure what's going on.
The jsx:
render: function() {
return(
<div className="_Sidebar">
<div className="top">
<i
className="menuIcon fa fa-bars"
onClick={() => this.handleClick()}>
</i>
<UserName />
</div>
{this.state.show ?
<ReactCSSTransitionGroup
transitionName="example"
transitionAppear={true}
transitionLeave={true} >
<div key={"slidebar"} className="sidebar">
{this.handleItems()}
</div>
</ReactCSSTransitionGroup>
: null}
</div>
);
}
And the css:
.example-appear {
left: -230px;
transition: left .9s ease-in;
}
.example-appear.example-appear-active {
left: 0px;
}
.example-leave {
left: 0px;
transition: left .9s ease-out;
}
.example-leave.example-leave-active {
left: -230px;
}
really I tried in your code there is a thing not right in putting ReactCSSTransitionGroup tag, so I attached my code , its woke correct, you can use it directly and put your data,
import React from 'react';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
var Test = React.createClass({
getInitialState: function () {
return { active: true };
},
onToggle: function () {
this.setState({active: !this.state.active});
},
render: function() {
return (
<div>
<ReactCSSTransitionGroup
transitionName="fade"
transitionEnterTimeout={500}
transitionLeaveTimeout={300}>
{!this.state.active && ( <h2> Test Asmaa Almadhoun </h2>)}
</ReactCSSTransitionGroup>
<div className="chatBTN" onClick={this.onToggle}>
<img src="../src/contents/images/svg/chat.svg"/>
</div>
</div>
);
}
});
export default Test;
CSS File
.chatBar{
position: fixed;
height: 320px;
z-index: 0;
right: 0;
top: 40px;
width: 150px;
text-align: center;
display: block;
transform: translateY(-40px);
}
.fade-enter {
transform: translateY(-88%);
}
.fade-enter-active {
top: 0;
transform:translateY(-40px);
transition: .5s ease-in all;
}
.fade-leave {
transform: translateY(-40px);
}
.fade-leave-active {
transform: translateY(-88%);
transition: 0.3s ease-out all;
}

Resources