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
Related
So I'm new to React and I come from an Angular background where all we have is classes.
I'm now trying to figure out if I can add styling to my html when using a class.
The React docs (regarding Drawers) are using a function to create the component. But I'm used to using classes instead of functions.
So question, is it possible to add styling to the element with the way I'm trying it now (and in a class)?
const useStyles = makeStyles({
list: {
width: 250,
}
});
export default class NavBar extends Component<any, any> {
constructor(props: any) {
super(props);
this.state = { open: false };
}
// Will complain about this
readonly classes = useStyles();
render() {
return (
<div className={this.classes.list}>test</div>
)
}
}
Error: Invalid hook call. Hooks can only be called inside of the body
of a function component. This could happen for one of the following
reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See ... for tips about how to debug and
fix this problem
This is how you can define inline styling in react using style object.
const useStyles = {
list: {
width: 250,
color: 'red'
}
};
class NavBar extends React.Component {
constructor(props) {
super(props);
this.state = { open: false };
}
render() {
return (
<div style={useStyles.list}>test</div>
)
}
}
Background
I am trying to make an element disappear after the animation ends (I am using animate.css to create the animations).
The above 'copied' text uses animated fadeOut upon clicking the 'Copy to Journal Link'. Additionally, the above demo shows that it takes two clicks on the link to toggle the span containing the text 'copied' from displayed to not displayed.
According to the animate.css docs, one can also detect when an animation ends using:
const element = document.querySelector('.my-element')
element.classList.add('animated', 'bounceOutLeft')
element.addEventListener('animationend', function() { doSomething() })
My Problem
However, within the componentDidMount() tooltip is null when attempting to integrate what animate.css docs suggest.
What am I doing wrong? Is there a better way to handle this behavior?
ClipboardBtn.js
import React, { Component } from 'react'
import CopyToClipboard from 'react-copy-to-clipboard'
class ClipboardBtn extends Component {
constructor(props) {
super(props)
this.state = {
copied: false,
isShown: true,
}
}
componentDidMount() {
const tooltip = document.querySelector('#clipboard-tooltip')
tooltip.addEventListener('animationend', this.handleAnimationEnd)
}
handleAnimationEnd() {
this.setState({
isShown: false,
})
}
render() {
const { isShown, copied } = this.state
const { title, value } = this.props
return (
<span>
<CopyToClipboard onCopy={() => this.setState({ copied: !copied })} text={value}>
<span className="clipboard-btn">{title}</span>
</CopyToClipboard>
{this.state.copied ? (
<span
id="clipboard-tooltip"
className="animated fadeOut"
style={{
display: isShown ? 'inline' : 'none',
marginLeft: 15,
color: '#e0dbda',
}}
>
Copied!
</span>
) : null}
</span>
)
}
}
export default ClipboardBtn
Using query selectors in React is a big NO. You should NEVER do it. (not that that's the problem in this case)
But even though it's not the problem, it will fix your problem:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
render() {
return <div ref={this.myRef} />;
}
}
https://reactjs.org/docs/refs-and-the-dom.html
componentDidMount gets called only once during the inital mount. I can see that in the inital component state, copied is false, hence #clipboard-tooltip never gets rendered. That is why tooltip is null.
Instead try this :
componentDidUpdate(prevProps, prevState) {
if(this.state.copied === true && prevState.copied === false) {
const tooltip = document.querySelector('#clipboard-tooltip')
tooltip.addEventListener('animationend', this.handleAnimationEnd)
}
if(this.state.copied === false && prevState.copied === true) {
const tooltip = document.querySelector('#clipboard-tooltip')
tooltip.removeEventListener('animationend', this.handleAnimationEnd)
}
}
componentDidUpdate gets called for every prop/state change and hence as soon as copied is set to true, the event handler is set inside componentDidUpdate. I have added a condition based on your requirement, so that it doesn't get executed everytime. Feel free to tweak it as needed.
Something that used to be simple is quite complicated when you don't know the React way.
I'm trying to create a component to act like a sticky header or footer when it reaches the top of the page.
At the moment I'm quite content in adding the below:
componentDidMount() {
window.addEventListener('scroll', this.onScroll, false);
}
componentWillUnmount() {
window.removeEventListener('scroll', this.onScroll, false);
}
However I've hit a wall to how I get the scroll top position of a styled component. So lets say I had a styled component called Container and that outputted a form I usually just add a data attribute and do something like the below:
const container = document.getElementbyTag("data-sticky-container")
const position = container.offsetTop
How would I go about doing this in React?
Update
Now using ref. I've fallen into a problem where current isn't defined:
constructor(props) {
super(props);
this.optionBox = React.createRef();
}
componentDidMount() {
window.addEventListener('scroll', this.onScroll, false);
}
componentWillUnmount() {
window.removeEventListener('scroll', this.onScroll, false);
}
onScroll() {
console.log(this.optionBox.current.offsetTop);
}
In react you would use a reference for your component: https://reactjs.org/docs/refs-and-the-dom.html
You would have something like this:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
getOffset = () => {
console.log(this.myRef.current.offsetTop);
}
render() {
return <Container ref={this.myRef} onClick={this.getOffset} />;
}
}
And now you can access the container offsetTop by this.myRef.current.offsetTop inside of your onScroll function like in getOffset
Another option is to use an innerRef. This can be better because it will give you a reference to the DOM node and not just a wrapper. See this answer.
You would then have more direct access to properties like scrollTop.
Unlike ref, innerRef uses a callback:
In your case it would look like:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef;
}
getOffset = () => {
console.log(this.myRef.offsetTop);
}
render() {
return <Container
innerRef={element => this.textInput = element}
onClick={this.getOffset} />;
}
}
You can add a ref
to the component for which u want an offsetTop. This ref will have all the computed css values of that component.
I'm having trouble figuring out the problem with the following code. I'm trying to change the prop animating on a ProgressBarAndroid, and make it toggle every second. The code works as intended if I set loading to true in my constructor, but not if it's set to false (which is what I want, I don't want it to start animating right away). When it's set to false, the progressbar stays invisible all the time. Any ideas?
import React, { Component } from 'react';
import { ProgressBarAndroid } from 'react-native';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {loading: false}; // works if it is set to true here instead
// Toggle the state every second
setInterval(() => {
this.setState({loading: !this.state.loading});
}, 1000);
}
render() {
return (
<ProgressBarAndroid animating={this.state.loading}></ProgressBarAndroid>
);
}
}
This is actually an issue I had with ProgressBarAndroid a few weeks back. Once initiated as false, I was never able to set it back true.
The quick and dirty solution I had at the time was to just to move the state change outside of the animating prop.
In your case, changing this:
<ProgressBarAndroid animating={this.state.loading}></ProgressBarAndroid>
to this:
<View>
{ this.state.loading ? <ProgressBarAndroid/> : null }
</View>
and make sure to also include View from react-native.
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.