React CSS Transition Group, not working? - reactjs

Apologies for the simplicity of this problem, but I am new to React and trying to implement a simple CSSTransitionGroup to hide/show an element, but with a fade, slide, etc. The documentation seems very straightforward, but for some reason the following code will not work for me.
While the .box toggles between being there or not, I do not see any of the CSS transitions in place on enter and on leave.
class Demo extends React.Component{
constructor(props) {
super(props);
this.state = { visible: false };
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
this.setState({ visible: ! this.state.visible });
}
render() {
return <div>
<button onClick={this.handleClick}>{this.state.visible ? 'Slide up' : 'Slide down'}</button>
<ReactCSSTransitionGroup transitionName="example">
{ this.state.visible ? <div className='box' /> : null }
</ReactCSSTransitionGroup>
</div>
}
}
.box {
width: 200px;
height: 100px;
background: green;
margin-top: 10px; }
.example-enter {
height: 0px; }
.example-enter.example-enter-active {
height: 100px;
-webkit-transition: height .3s ease; }
.example-leave.example-leave-active {
height: 0px;
-webkit-transition: height .3s ease; }
I must be doing something wrong, as I can see this basic demo working in other online examples, but cannot replicate myself. Please let me know how to get my CSS transitions.
Thx internet

Did you check the console for error output? Because when I run your code I get this:
"Warning: Failed propType: transitionEnterTimeout wasn't supplied to
ReactCSSTransitionGroup: this can cause unreliable animations and
won't be supported in a future version of React. See
xxx for more
information. Check the render method of Demo."
"Warning: Failed
propType: transitionLeaveTimeout wasn't supplied to
ReactCSSTransitionGroup: this can cause unreliable animations and
won't be supported in a future version of React. See
xxx for more
information. Check the render method of Demo."
Adding the two missing props and it works fine.
class Demo extends React.Component{
constructor(props) {
super(props);
this.state = { visible: false };
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
this.setState({ visible: ! this.state.visible });
}
render() {
return <div>
<button onClick={this.handleClick}>{this.state.visible ? 'Slide up' : 'Slide down'}</button>
<ReactCSSTransitionGroup transitionName="example" transitionEnterTimeout={300} transitionLeaveTimeout={300} >
{ this.state.visible ? <div className='box' /> : null }
</ReactCSSTransitionGroup>
</div>
}
}
transitionEnterTimeout and transitionLeaveTimeout both take a number representing the duration of the transition in milliseconds, hence "300" for a .3 second transition, which matches the CSS transitions you've specified.

Related

How to Prevent Parent Component State from Resetting to Original after Closing the Child Component

I have a search form with a lot of search components and a List container component which contains Item component to display the search results. When I click a selected Item, it pops up a Detail component. Right now, everything works fine except when clicking Close button inside the Detail component, the form gets reset and list of items also disappears. The Close button should just close the Detail component so I can select a different item in the list to view. What is the problem in my code? Thanks.
App.js
class App extends Component {
state={ showPopup: false,
selectedItem:'',
Items:[]};
togglePopup=()=> {
this.setState({
showPopup: !this.state.showPopup
});
}
onItemseSelect=(item)=>{
this.setState({selectedItem:item});
};
render(){
const Items=['aa','bb','cc'];
return(
<List
Items={this.state.Items}
onItemSelect={this.onItemSelect}
onClick={this.togglePopup}
/>
{this.state.showPopup ?
<Detail
item={this.state.selectedItem}
closePopup={this.togglePopup.bind(this)}
/>
: null
}
);
}
}
List.js
import React from 'react';
import Item from './Item';
const List=({Items,onItemSelect})=>{
const renderedList= Items.map(item=>{
return (
<Item key={item.ID} item={item} onItemSelect={onItemSelect} />
);
})
return <div>
{renderedList}</div>
}
export default List;
Item.js
import React from 'react';
const Item=({item, onItemSelect})=>{
return <div onClick={()=>onItemSelect(item)} >
<div class="content">
<div class="header">
{/*display contents*/}
View More
</div>
</div>
};
export default Item;
Detail.js
import React from 'react';
const Detail=({item,closePopup})=>{
if (!item){
return <div>loading</div>
}
return (
<div className='popup'>
<div className='popup_inner'>
<p>
{/*contents here*/}
</p>
<button onClick={()=>closePopup}>close me</button>
</div>
</div>);
};
export default Detail;
css code:
.popup {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
background-color: rgba(0,0,0, 0.5);
}
.popup_inner {
position: absolute;
left: 25%;
right: 25%;
top: 25%;
bottom: 25%;
margin: auto;
background: white;
}
No error message. The form resets to the original state.
I think the problem is here only, I doubt how your list items are rendered first time only.
Items:[]
render(){
const Items=['aa','bb','cc']; //static values which are not in use
return(
<List
Items={this.state.Items} //You are using state which is blank
onItemSelect={this.onItemSelect}
onClick={this.togglePopup}
/>
...
)
}
Complete Running code is like this.

ReactCSSTransitionGroup in child component not working

I am trying to map over state and add a transition to each element so that the elements appear on mounting the dom.
There are two components relevant to this: PricingPage and PricingCard.
The code for PricingPage is below:
import React, {Component} from 'react';
import PricingCard from './PricingCard';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
class PricingPage extends Component {
constructor(props) {
super(props);
this.state={
pricingContent:[{title:'1Abstract Art Pricing', text:`Each piece of artwork is individually priced. I base this on how much work and effort I put into the picture, basically I'm just using my own criteria.`},{title:'2Abstract Art Pricing', text:`Each piece of artwork is individually priced. I base this on how much work and effort I put into the picture, basically I'm just using my own criteria.`},{title:'3Abstract Art Pricing', text:`Each piece of artwork is individually priced. I base this on how much work and effort I put into the picture, basically I'm just using my own criteria.`},{title:'4Abstract Art Pricing', text:`Each piece of artwork is individually priced. I base this on how much work and effort I put into the picture, basically I'm just using my own criteria.`},{title:'5Abstract Art Pricing', text:`Each piece of artwork is individually priced. I base this on how much work and effort I put into the picture, basically I'm just using my own criteria.`},{title:'6Abstract Art Pricing', text:`Each piece of artwork is individually priced. I base this on how much work and effort I put into the picture, basically I'm just using my own criteria.`}]
}
}
render () {
return (
<div className='pricing-page-wrapper'>
<div className='navbar-background'></div>
<h1 className='pricing-title'>Pricing</h1>
<ReactCSSTransitionGroup transitionName="pricing" transitionEnterTimeout={700} transitionLeaveTimeout={700}>
<div className='pricing-wrapper'>
{this.state.pricingContent.map((item,i) => {
return(
<PricingCard key={item.title} title={item.title} text={item.text} />
)
})}
</div>
</ReactCSSTransitionGroup>
</div>
)
}
}
export default PricingPage;
.pricing-page-wrapper{
min-height: 75vh;
width:80%;
margin: 0 auto;
.pricing-title{
color:#949494;
}
.pricing-wrapper{
opacity:1;
}
.pricing-enter {
height: 0px;
opacity: 0;
}
.pricing-enter.pricing-enter-active {
height: 90px;
opacity: 1;
transition: 700ms;
}
.pricing-leave {
opacity: 1;
height: 90px;
}
.pricing-leave.pricing-leave-active {
opacity: 0;
height: 0;
transition: 700ms;
}
}
PricingCard page:
import React, {Component} from 'react';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
class PricingCard extends Component {
constructor(props){
super(props);
}
render () {
return (
<div className='pricing-card'>
<h3 className='pricing-card-title'>{this.props.title}</h3>
<div className='pricing-card-text'>{this.props.text}</div>
</div>
)
}
}
export default PricingCard;
.pricing-card{
border-radius: 15px;
border: 1px solid #949494;
padding: 20px;
height:90px;
margin: 10px 0 10px 0;
.pricing-card-title{
color:#949494;
}
.pricing-card-text{
color:#949494;
}
}
I cant seem to figure out what I'm doing wrong, any help would be appreciated.
You must use some unique key for an item in list, it's array index won't work. You can use item.title for example.
You must wrap all the elements group into single ReactCSSTransitionGroup, not every individual element.
ReactCSSTransitionGroup will track them by their keys, so it must be mounted and have all that unique keys for group elements. So take it out of PricingCard.render to PricingPage.render.
class PricingPage extends Component {
render () {
return (
<div className='pricing-page-wrapper'>
<div className='navbar-background'></div>
<h1 className='pricing-title'>Pricing</h1>
<ReactCSSTransitionGroup transitionName="pricing"
transitionEnterTimeout={700} transitionLeaveTimeout={700}>
{this.state.pricingContent.map((item,i) => {
return(
<PricingCard key={item.title}
title={item.title} text={item.text} />
)
})}
</ReactCSSTransitionGroup>
</div>
)
}
}
So i added transitionAppear={true} and used the following css selectors in my scss file:
.pricing-appear {
opacity: 0.01;
}
.pricing-appear.pricing-appear-active {
opacity: 1;
transition: opacity .5s ease-in;
}
The issue was that i wanted to animate on initial mounting of the components.

ReactJS ReactCSSTransitionGroup why does it work one component but not another?

Trying to figure out why this ReactCSSTransitionGroup animation works:
class SlideExample extends React.Component{
constructor(props) {
super(props);
this.state = { visible: false };
this.handleClick = this.handleClick.bind(this)
}
handleClick() {
this.setState({ visible: ! this.state.visible });
}
render() {
return <div>
<button onClick={this.handleClick}>{this.state.visible ? 'Slide up' : 'Slide down'}</button>
<ReactCSSTransitionGroup
transitionName="example"
transitionEnterTimeout={300}
transitionLeaveTimeout={300}>
{
this.state.visible
? <div className='panel'>
<ul className="project-list">
<li>one</li>
<li>two</li>
<li>three</li>
</ul>
</div>
: null
}
</ReactCSSTransitionGroup>
</div>
}
}
const ProjectList = (props) => {
return(
<div className="ProjectList">
<SlideExample />
</div>
);
}
But not like this:
class App extends Component {
constructor() {
super();
this.state = {
_isProjectNavOpen: true,
}
}
_toggleProjectNav() {
this.setState(prevState => ({
_isProjectNavOpen: !prevState._isProjectNavOpen,
}));
}
render() {
return(
<div className="App">
<Router>
<div className="main-content">
<Route path="/projects" component={(props, state, params) =>
<ProjectList
_toggleProjectNav={this._toggleProjectNav}
_isProjectNavOpen={this.state._isProjectNavOpen}
{...props} />} />
</div>
</Router>
</div>
);
}
}
const ProjectList = (props) => {
return(
<div className="ProjectList">
<div className="center title" onClick={props._toggleProjectNav} id="Menu">Menu</div>
<ReactCSSTransitionGroup
transitionName="example"
transitionEnterTimeout={300}
transitionLeaveTimeout={300}>
{
props._isProjectNavOpen
? <div className='panel'>
<ul className="project-list">
<li>xx one</li>
<li>xx two</li>
<li>xx three</li>
</ul>
</div>
: null
}
</ReactCSSTransitionGroup>
</div>
);
}
The CSS:
.panel {
width: 200px;
height: 100px;
background: green;
margin-top: 10px;
}
.example-enter {
height: 0px;
}
.example-enter.example-enter-active {
height: 100px;
-webkit-transition: height .3s ease;
}
.example-leave.example-leave-active {
height: 0px;
-webkit-transition: height .3s ease;
}
_toggleProjectNav is a prop passed down from a parent component that toggles the _isProjectNavOpen state true/false; it works in that the panel does hide/show, but without the animation... does it have to do with the state being passed from the parent? Trying to understand how ReactCSSTransitionGroup works.
Thanks!
This code works for me. In this case this is not a ReactCSSTransitionGroup issue. The issue is most probably related to the CSS. Pay attention, some browsers require initial height value before the transition. So you need something like this:
.example-leave {
height: 100px;
}

Changing style of a button on click

Is it possible to change background-color of my button onClick function?
ex. click background-color: black, another click background-color: white
I've tried something like this.style, no result.
I've managed to get overlay working and insert needed data inside of it.
But didn't managed to find any post that could help me.
I am using react-bootstrap.
This is my code.
const metaDataOverlay = (
<div>
<style type="text/css">{`
.btn-overlay {
background-color: white;
margin-right: -15px;
margin-left: -15px;
padding-bottom: -20px;
padding: 0;
}
`}</style>
<ButtonToolbar>
<ButtonGroup>
<OverlayTrigger trigger={['hover', 'focus']} placement="left" overlay={popoverHoverFocus}>
<Button bsStyle="overlay" onClick={ clicked } onKeyPress={ keypress }>
<div className={bemBlocks.item().mix(bemBlocks.container("item"))} data-qa="hit">
<a href={url} onClick={(e)=>{e.preventDefault(); console.log("123")}}>
<div>
<img data-qa="poster" className={bemBlocks.item("poster")} src={result._source.poster} width="240" height="240"/>
</div>
</a>
</div>
</Button>
</OverlayTrigger>
</ButtonGroup>
</ButtonToolbar>
</div>
)
You can try to use state to store the color. Maybe this would give you the idea how to solve the problem :
class Test extends React.Component {
constructor(){
super();
this.state = {
black: true
}
}
changeColor(){
this.setState({black: !this.state.black})
}
render(){
let btn_class = this.state.black ? "blackButton" : "whiteButton";
return (
<button className={btn_class} onClick={this.changeColor.bind(this)}>
Button
</button>
)
}
}
React.render(<Test />, document.getElementById('container'));
Here is a fiddle.
You also have access to event and current target of the event
handleClick = (event) => {
// accessible
event.target.style
event.target.classList //to change style via css
}
Here is another solution
changeStyles = () => {
let element = document.getElementById('button')
ReactDOM.findDOMNode(element).style.backgroundColor = this.state.isClicked?'black' : 'white'
}
In this way you can change only needed style property preventing duplicates in CSS.
This is how you can access
handleClick=(e)=>{
console.log("this is working fine");
e.preventDefault();
e.target.style.color = 'black'
console.log(e.target);
}
If you want more dynamically you can initialize state with some default value of style afterwords use setState function to update your state
Add this to your Tooltip
<Tooltip cursor={{ fill: 'transparent' }} />
Here is another solution :
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
BackgroundColor: "BLACK"};
};
render(){
return (
<div className='app'>
<button className={this.state.BackgroundColor === "BLACK" ? "Black" : "nothing"}
onClick={() => {this.setState({BackgroundColor: "WHITE"})}}>CHANGE TO BLACK</button>
<button className={this.state.BackgroundColor === "WHITE" ? "White" : "nothing"}
onClick={() => {this.setState({BackgroundColor: "BLACK"})}}>CHANGE TO WHITE</button>
</div>
);
}
}
export default App;
This code will help you to hide the ex black button and change with new button with white background. This function will also work for the new white button. If maybe you just want to change background-color of button without repeat situation you can also try to change conditional state in render
render(){
return (
<div className='app'>
<button className={this.state.BackgroundColor === "BLACK" ? "Black" : "White"}
onClick={() => {this.setState({BackgroundColor: "WHITE"})}}>CHANGE TO BLACK</button>
</div>
);
}
}
Here is the CSS :
*{
margin: 0;
padding: 0;
}
.app{
display: flex;
justify-content: center;
align-items: center;
}
.Black{
background-color: black;
color: white;
}
.White{
background-color: white;
color: black;
}
.nothing{
display: none;
}

Can I use ReactTransitionGroup for creating transitions on update?

Looking at the component docs, react components has three items in their lifecycle: mount, unmount and update. React transition groups seems to be the most common way to apply transitions and animations in react. Can it be used on update aswell, (ie statechange) or only when an item is mounted/unmounted?
Yes you can add transition on state change. You need to provide a key to the child element which will change on state update.
From docs :
You must provide the key attribute for all children of ReactCSSTransitionGroup, even when only rendering a single item. This is how React will determine which children have entered, left, or stayed.
Thus, you can do something like this :
class Container extends React.Component {
constructor(props) {
super(props);
this.state = {
number: 0
};
}
handleClick(e){
this.setState({number: this.state.number + 1});
}
render(){
return (
<div className='container'>
<CSSTransitionGroup transitionName="example" transitionAppear={true} transitionAppearTimeout={500} transitionEnterTimeout={500} transitionLeaveTimeout={300}>
<div className="number" key={this.state.number}>{this.state.number}</div>
</CSSTransitionGroup>
<button onClick={this.handleClick.bind(this)}>Click Me!</button>
</div>
)
}
}
React.render(<Container />, document.getElementById('container'));
Css
.example-enter {
opacity: 0.01;
}
.example-enter.example-enter-active {
opacity: 1;
transition: opacity 500ms ease-in;
}
.example-leave {
opacity: 1;
}
.example-leave.example-leave-active {
opacity: 0.01;
transition: opacity 300ms ease-in;
}
.example-appear {
opacity: 0.01;
}
.example-appear.example-appear-active {
opacity: 1;
transition: opacity .5s ease-in;
}
Here is fiddle.

Resources