ReactCSSTransitionGroup doesn't work - javascript

I have some trouble with ReactCSSTransitionGroup. I want to show up some kind of Modal form with some fancy animations when user clicks the button on the page. This is the code:
import React from 'react';
import ReactDOM from 'react-dom';
import AppConfig from './AppConfig';
export default class AppTop extends React.Component {
constructor(props) {
super(props);
this.state = { openConfig: false };
}
toggleConfig() {
this.setState({ openConfig: !this.state.openConfig });
}
render() {
// only shows up openConfig is true
const domAppConfig = this.state.openConfig ? <AppConfig ... /> : '';
return (
<div>
... //some elements
<button onClick={this.toggleConfig.bind(this)}>Config</button>
{ domAppConfig }
</div>
);
}
}
This is AppTop component. You can see the button component inside of render, on click this, openConfig state will toggle. Now this is AppConfig component:
import React from 'react';
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
export default class AppConfig extends React.Component {
render() {
return (
<ReactCSSTransitionGroup
transitionName="anim-app-config"
transitionEnterTimeout={300}
transitionLeaveTimeout={300}
>
<div id="app-config">
<h1>App Config</h1>
<p>Helloworld!</p>
</div>
</ReactCSSTransitionGroup>
);
}
}
component is quite simple, just print some texts. And this is the style of '.anim-app-config':
.anim-app-config-enter {
opacity: .1;
}
.anim-app-config-enter.anim-app-config-enter-active {
opacity: 1;
transition: opacity 300ms ease-in;
}
.anim-app-config-leave {
opacity: 1;
}
.anim-app-config-leave.anim-app-config-leave-active {
opacity: .1;
transition: opacity 300ms ease-in;
}
when I run this App, animation doesn't work. Just component shows up and hides immediately, without animations. I searched about this, and I tried adding a key into AppConfig Component, it didn't worked. Wrapping one more div didn't work either. Adding transitionAppear={true} also not worked for me.
Am I wrong using ReactCSSTransitionGroup component? Or am I something missed? It is hard to add animation on React to me, so please gimme some advice. Thanks.

You should have ReactCSSTransitionGroup in your Apptop because ie the point where it will render or not. MAking it inside AppConfig wont have any animation since it will render it as a straight component.
import React from 'react';
import ReactDOM from 'react-dom';
import AppConfig from './AppConfig';
export default class AppTop extends React.Component {
constructor(props) {
super(props);
this.state = { openConfig: false };
}
toggleConfig() {
this.setState({ openConfig: !this.state.openConfig });
}
render() {
// only shows up openConfig is true
const domAppConfig = this.state.openConfig ? <AppConfig ... /> : '';
return (
<div>
... //some elements
<button onClick={this.toggleConfig.bind(this)}>Config</button>
<ReactCSSTransitionGroup
transitionName="anim-app-config"
transitionEnterTimeout={300}
transitionLeaveTimeout={300}
>
{domAppconfig}
</ReactCSSTransitionGroup>
</div>
);
}
}

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.
Try adding something like key="1" to <div id="app-config"> and see if it helps.

Related

Get part of className using JQuery returns null

I have this piece of code in codesandbox, but its returning null when i try to get part of className from div using JQuery. How can i make it work?
Codesandbox Code example
import React, { Component } from "react";
import "./styles.css";
import $ from "jquery";
export default class App extends Component {
componentDidMount() {
$(".MainContainer").on("mousedown", function(evt) {
$(".MainContainer").on("mouseup mousemove", function handler(evt) {
alert(
$(this)
.attr("class")
.match(/\bsizes[^\s]+\b/)
);
});
});
}
constructor(props) {
super(props);
this.state = {};
}
render() {
return <div className="MainContainer sizes" />;
}
}
The css class:
.MainContainer {
background: #282c34;
position: relative;
display: flex;
min-height: 100vh;
}
First of all, I highly recommend to not use jQuery with your react code, but if you have to, then use ref utility to handle dom events and let React handles them, instead of directly involving jQuery.
In addition to using multiple events for on() method, pass an object with multiple methods instead of nesting them.
I propose using console.log() instead of alert() method to retrieve any data or DOM elements to prevent mistakes.
Finally I commented your regex to see it works or not. The regex is returning null but the rest of the code is working.
View on Codesandbox
PS: React way is following.
import React, { Component } from "react";
import "./styles.css";
import $ from "jquery";
export default class App extends Component {
constructor(props) {
super(props);
this.state = {};
this.myRef = React.createRef();
}
componentDidMount() {
$(this.myRef.current).on({
mousedown: function(evt) {
console.log(
$(this).attr("class")
// .match(/\bsizes[^\s]+\b/)
);
},
mouseup: function(evt) {
console.log(
$(this).attr("class")
// .match(/\bsizes[^\s]+\b/)
);
},
mousemove: function(evt) {
console.log(
$(this).attr("class")
// .match(/\bsizes[^\s]+\b/)
);
}
});
}
render() {
return <div className="MainContainer sizes" ref={this.myRef} />;
}
}
The React Way
As I recommended to use only react for handling DOM elements, I'll post the React code, too.
Note: I made a loop through the events but you can just repeat events by hand to decrease complexity. Also, I used find() to loop trough DOMTokenList values and checked them to match the regex. I commented that line to prevent the response of the regex checking.
View on Codesandbox
import React, { Component } from "react";
import "./styles.css";
export default class App extends Component {
constructor(props) {
super(props);
this.state = {};
this.myRef = React.createRef();
}
componentDidMount() {
this.eventHandler();
}
eventHandlerCallback = e => {
console.log(
Object.values(e.target.classList)
// .find(item => item.match(/\bsizes[^\s]+\b/))
);
};
eventHandler = () => {
const events = ["mousedown", "mouseup", "mousemove"];
events.map(e =>
this.myRef.current.addEventListener(e, this.eventHandlerCallback)
);
};
render() {
return <div className="MainContainer sizes" ref={this.myRef} />;
}
}

Rendering CSS transition when React element's text changes

Here is a snippet of my code base - https://codesandbox.io/s/transition-code-1wr5z
Currently, via changing classes and CSS transitions, text fades in when a new Paragraph component is loaded in and mounted.
However, I wish to also have this transition occur when the text prop within the Paragraph component changes.
Doing this within the lifecycle update or render just triggers an infinite update loop.
Not sure where to go from here as most discussions I can find are about tackling the functionality of getting the text to fade in on load, rather than on update.
Here is the snippet solution
import React, { Component } from "react";
import "./Style.css";
class Paragraph extends Component {
constructor(props) {
super(props);
this.state = {
open: false
};
}
componentDidMount() {
setInterval(() => {
this.fade();
}, 1000);
}
fade() {
this.setState({ open: !this.state.open });
}
render() {
const text = this.props.text;
const classes = this.state.open ? "greenCls" : "redCls";
return <div className={classes}>{text}</div>;
}
}
export default Paragraph;
css class
.redCls {
background: red;
}
.greenCls {
background: green;
}
Please try using setTimeout to fix this issue
https://codesandbox.io/s/transition-code-fgmyq

How do I reveal my button only when on a specific component?

I want to show the Logout button on the same row of the title but only when the user has made it to Home component.
In other words, I don't want to show the logout button at all times, especially when the user's at the login screen. I want it to show on the same row of the title only when they've logged in successfully and they're in Home
How would I achieve this? My head hurts from trying to make this work :(
Below's what I've tried so far, among other things.
import React, {Component} from 'react';
import classes from './Title.css';
import LogoutButton from '../../containers/LogoutButton/LogoutButton';
import Home from '../../components/Home/Home';
class Title extends Component {
constructor(props) {
super(props);
this.state = {
show: false,
showLogoutButton: true
};
}
showButton() {
this.setState({show: true});
if(this.state.show) {
return <LogoutButton/>;
} else {
return null;
}
}
render() {
return(
<div>
{ this.state.showLogoutButton ? this.showButton : null }
<h1 className={classes.Title}>Pick Ups</h1>
</div>
);
}
}
export default Title;
You can try something like below. You don't need to deal with function and modifying states.
You can simply do like below
import classes from './Title.css';
import LogoutButton from '../../containers/LogoutButton/LogoutButton';
import Home from '../../components/Home/Home';
class Title extends Component {
constructor(props) {
super(props);
this.state = {
showLogoutButton: this.props.authenticated
};
}
render() {
const { showLogoutButton } = this.state;
return(
<div className="row" style={{"display" :"flex"}}>
{ showLogoutButton && <LogoutButton/>}
<h1 className={classes.Title}>Pick Ups</h1>
</div>
);
}
}
export default Title;
Note: When you modify state using setState the state value will be updated only after render so you can't directly check immediately modifying the value.

React: How to call a component function on it's own render from another component?

I'm trying to implement methods to the React import of PESDK (PhotoEditorSDK).
I have an App.js that imports Header, BodyLeft and BodyMiddle without relation between them.
BodyMiddle.js is a template component that renders :
// src/components/BodyMiddle/index.js
import React, { Component } from "react";
import "./BodyMiddle.css";
class BodyMiddle extends Component {
constructor(props){
super(props);
}
handleClick(e) {
e.preventDefault();
// Nothing yet
}
render() {
return (
<div id="BodyMiddle">
<div><button id="resetEditor" onClick={(e) => this.handleClick(e)}>Reset Editor</button></div>
<div class="photo-editor-view"></div>
</div>
);
}
}
export default BodyMiddle;
PhotoEditor.js is the component that calls the PESDK :
// src/components/PhotoEditor/index.js
import React from 'react'
import ReactDOM from 'react-dom'
window.React = React
window.ReactDOM = ReactDOM
import "./PhotoEditor.css";
import "photoeditorsdk/css/PhotoEditorSDK.UI.ReactUI.min.css";
import PhotoEditorUI from 'photoeditorsdk/react-ui'
class PhotoEditor extends React.Component {
constructor(props){
super(props);
}
resetEditor(){
// Empty the image
return this.editor.ui.setImage(new Image());
}
render() {
const { ReactComponent } = PhotoEditorUI;
return (
<div>
<ReactComponent
ref={c => this.editor = c}
license='licence_removed_for_snippet'
assets={{
baseUrl: './node_modules/photoeditorsdk/assets'
}}
editor={{image: this.props.image }}
style={{
width: "100%",
height: 576
}} />
</div>)
}
}
export default PhotoEditor;
Note that the photo-editor-view div class is rendered in BodyLeft.js, by calling the following code and it works well:
ReactDOM.render(<PhotoEditor ref={this.child} image={image} />, container);
Where container is (and I pass an image somewhere else) :
const container = document.querySelector('.photo-editor-view');
What I'm trying to achieve
I would like to keep the reset Button inside BodyMiddle, which is independant and called from App.js, in order to call the PhotoEditor component on the method resetEditor() from anywhere in my app.
That way I could have separated template files that interract with each other.
I've done research and I did not really find an answer yet, I know that React might not be the lib for that, but what are the options ? I see more and more React live apps running with a lot of components interacting, I'm curious.
Thank you for your time !
Best regards
You can use ref on PhotoEditor and save that ref in App, and in the App you can have a method called onResetEditor which calls the ref.resetEditor.
Now you can pass onResetEditor to BodyMiddle or any other component.
Read more about refs in React https://reactjs.org/docs/refs-and-the-dom.html

When importing styles into a React component, how do I assign a className that updates based off state?

I'm using style-loader to inject css modularly into my components ({style.exampleClassName}).
I want to display a loader for a set amount of time then display an image (at least 16 of these components in a grid pattern).
My current component looks like this:
// Child Component
/**
*
* Sets up props for the child (icon)
*
*/
import React, { Component } from 'react';
import styles from './styles.css';
class Child extends React.Component {
constructor(props) {
super(props);
this.state = {
hidden : "shown",
loading: "loading"
};
}
componentDidMount() {
const self = this;
const wait = this.props.wait;
console.log('mounted');
setTimeout(() => {
console.log('timeout working ' + wait);
self.setState({
hidden: "hidden",
loading: "loaded"
});
}, wait);
}
render() {
const hidden = `styles.${this.state.hidden}`;
const loading = `styles.${this.state.loading}`;
return (
<div>
<link rel="stylesheet" type="text/css" href="./app/components/socialgrid/styles.css" />
<div className={this.state.hidden}>
<p>Loading...</p>
</div>
<div className={this.state.loading}>
<p>Child - {this.props.wait}ms</p>
</div>
</div>
)
}
};
export default Child;
// Parent
/**
*
* Individual Icon Component
*
*/
import React, { Component } from 'react';
import cx from 'classnames';
import Img from 'components/Img';
import Child from './Child';
// import Fb from './Fb.png';
class IndIcon extends React.Component{
render() {
return (
<div>
<div>
<Child wait={1000} />
<Child wait={5000} />
<Child wait={4000} />
</div>
</div>
)
}
};
export default IndIcon;
.hidden,
.loading{
display: none;
}
Normally my styles would inject themselves by className={styles.exampleClassName} but here I'm running into the issue of the class not being injected because the class changes based of state (as I said above, just trying different wording to be clear).
I want to assign more than just the display:none element so I do need classes on these components.
Help would be appreciated. Thanks!
You were not too far off... you just weren't passing your const variables into your JSX. Try modifying your render function as follows (amendments highlighted with bold):
render() {
const hidden = `styles.${this.state.hidden}`;
const loading = `styles.${this.state.loading}`;
return (
<div>
<link rel="stylesheet" type="text/css" href="./app/components/socialgrid/styles.css" />
<div className={hidden}>
<p>Loading...</p>
</div>
<div className={loading}>
<p>Child - {this.props.wait}ms
</div>
</div>
)
}
N.B.
Including your styles within the component in this way pollutes the global CSS namespace which can cause problems if you have styles if the same name defined elsewhere in the application ( by yourself or by another developer) which can give rise to unpredictable style behaviour
Even though I really wanted to update those classes on state change, I went this route instead and its way better:
import React, { Component } from 'react';
import Spinner from './Spinner';
import Icon from './Icon';
class Child extends React.Component {
constructor(props){
super(props);
this.state = {
loading: true
}
}
componentDidMount(){
const wait = this.props.wait;
setTimeout(() => {
this.setState({
loading: false
})
}, wait)
}
render() {
let content = this.state.loading ?
<div><Spinner /></div> :
<div><Icon /></div>;
return (
<div>{content}</div>
)
}
};
This way it loads components based off state change and a settimeout.

Categories