change opacity after 200ms in component style ReactJS - javascript

I using ReactJS framework and I try that, this component to change its style after 200ms from opacity 0 to opacity 1. Is it possible to do such a setTimeout?
<GreetingHeadline styles={?} id={this.props.user.id} />

Here's a working example that uses toggles between hidden/visible classes. I've added the transition so the effect can be more easily seen (200ms is a very short time) but you can remove it in your code.
class Test extends React.Component {
constructor() {
super();
this.state = { classes: 'hidden' };
}
componentDidMount() {
setTimeout(() => this.setState({ classes: 'visible' }), 200);
}
render() {
const { classes } = this.state;
return <div className={classes}>Text to be rendered</div>;
}
}
ReactDOM.render(<Test />, document.getElementById('container'));
.hidden { opacity: 0; }
.visible { opacity: 1; transition: opacity 1s linear;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="container"></div>

Setting a class is the easiest way to change the opacity. Here's an example that also uses an animation to "smooth" the transition.
https://codesandbox.io/s/j371123nq9

You can put a variable to the state of the GreetingHeadline's parent component:
constructor() {
this.state = {
transparent: true;
}
}
Then you can use setTimeout in the componentDidMount lifetime's method:
componentDidMount() {
this.setTimeout(() => {
this.setState({ transparent: false });
}, 200);
}
Finally you can use variable from the state within your GreetingHeadline component's props:
<GreetingHeadline
styles={{ opacity: this.state.transparent ? '0.7' : '1' }}
id={this.props.user.id}
/>

Related

React animate fade-in and fade-out with CSSTransitionGroup on conditionally rendered component

I have a card component that conditionally renders a check icon when this.state.isSelected is true. I want to animate the check icon when it renders. I also want to animate it when it leaves.
I have the following class component:
import { CSSTransitionGroup } from 'react-transition-group';
export default class AdoptablesFilterCard extends Component {
constructor(props) {
super(props);
this.state = {
isSelected: false,
cardHeader: props.cardHeader,
cardType: props.cardType,
}
//Click handler binding
this.handleClick = this.handleClick.bind(this);
}
handleClick = (e) => { //switches the state 'isSelected' when clicked
this.setState((prevState) => ({
isSelected: !prevState.isSelected
}))
}
render() {
const {isSelected} = this.state;
// let check;
// {isSelected ?
const check = <i className="far fa-check-circle"
key={this.state.cardHeader}></i>;
//:
//check = <div key={this.state.cardHeader}></div>}
return (
<div className="adoptables-filter-card" onClick={this.handleClick} ref={this.myRef}>
<div className="adoptables-filter-card-header">
{this.props.cardHeader}
</div>
<div className="adoptables-filter-card-body">
{(() => {
switch (this.props.cardType) {
case "animal": return(<i className={`fas fa-${this.props.cardHeader}`}></i>)
case "color": return(<div className="color-splotch" style={{background: this.props.cardHeader}}></div>)
}
})()}
</div>
{isSelected ? <CSSTransitionGroup
transitionName="icon"
transitionAppear={true}
transitionAppearTimeout={3000}
transitionEnter={false}
transitionLeave={false}>
{check}
</CSSTransitionGroup>
: null}
</div>
)
}
}
and the CSS classes for CSSTransitionGroup:
.icon-appear {
opacity: 0.01;
}
.icon-appear.icon-appear-active {
opacity: 1;
// transform: rotateY(360deg);
transition: opacity 3000ms ease-in;
}
This code works for rendering the check icon when the card is clicked and applying the animation (the 3000ms is so I can make sure it's there and activating). When the card is clicked again (de-selected) the check icon immediately disappears. I'd like to fade-out the check icon at this stage.
I found this while searching for an answer:
https://stackoverflow.com/questions/46916118/conditional-rendering-and-reactcsstransitiongroup-animation#=. You can see where I've commented out the conditional assignment of 'check'. It renders/hides the check icon, but the animation is not applied.
You can use CSSTransition. Replace this
{isSelected ? <CSSTransitionGroup
transitionName="icon"
transitionAppear={true}
transitionAppearTimeout={3000}
transitionEnter={false}
transitionLeave={false}>
{check}
</CSSTransitionGroup>
: null}
by
<CSSTransition
unmountOnExit
in={isSelected}
timeout={2000}
classNames="icon">
{check}
</CSSTransition>
style.css
.icon-enter {
opacity: 0;
}
.icon-enter-active {
opacity: 1;
transition: opacity 2000ms;
}
.icon-exit {
opacity: 1;
}
.icon-exit-active {
opacity: 0;
transition: opacity 2000ms;
}
You can see in CodeSandBox. Hope it helps

how to differentiate elements in React

I have few images which I need to zoom little bit (using CSS i.e. transform : scale(1.2);) on onMouseEnter and revert on onMouseLeave.
I have below code which is working.
CSS:-
.style {
transform : scale(1.2);
transition: transform .5s ease-out;
}
.shrink {
transform : scale(1);
transition: transform .5s ease-out;
}
Variable Declared in constructor:-
this.state = {
isHovered: false
};
JS Method:-
handleHover(){
this.setState({
isHovered: !this.state.isHovered
});
}
logical op in render method:-
const imgClass = this.state.isHovered ? 'profile-pic style' : 'profile-pic shrink';
And two images:-
<Image title ='one' src="pics/pic1.png" circle className={imgClass}
onClick={ () => this.props.methodRef('one')} height="70" width="100"
onMouseEnter={this.handleHover} onMouseLeave={this.handleHover}/>
<Image title ='two' src="pics/pic2.png" circle className={imgClass}
onClick={ () => this.props.methodRef('two') } height="70" width="100"
onMouseEnter={this.handleHover} onMouseLeave={this.handleHover}/>
This code is working perfectly as I am expecting it to be. But the problem is both the images are zooming in and out at the same time.
How can I differentiate mouse movement over different-different elements in ReactJs.
You can wrap the Image components into one of your own that you can control and add states to. Or maybe another way would be to have an array in your parent component like:
state: {
images:[{id: "image1", isHovered: false},
{id:"image2", isHovered: false}]
}
So you can have your ids in the components:
<Image id={this.state.images[0].id} ...rest />
then handleHover:
handleHover(e) => {
const images = [...this.state.images]; //copy your array
const index = images.findIndex(el => (el.id === e.target.id));
images[index].isHovered = !images[index].isHovered;
this.setState({images});
}
This is overly complex, though. You should wrap it inside another component and manage state from there.

How to make picture on focus in react-native

I have several pictures and onPress I want to make tapped picture bigger(on focus) make background dark with opacity like this.
If I will tap on this picture I wanna do roughly same thing but on mobile (react-native).
I believe you need to zoom the image from center and not from the top left. You can use this approach, and here is the pen for it: Codepen Link
class Profile extends React.Component {
constructor(props){
super(props);
this.state = {
height: '100%',
width: '100%',
flag: 0
};
}
render() {
var { pic } = this.props;
return (
<div>
<img src={pic} id="myImage" height={this.state.height} width={this.state.width} onClick={this.zoomHandler.bind(this)}/>
</div>
);
}
zoomHandler()
{
if(this.state.flag === 0)
{
document.getElementById("myImage").style.transform = "scale(2,2)";
this.setState({flag: 1});
}
else
{
document.getElementById("myImage").style.transform = "scale(1,1)";
this.setState({flag: 0});
}
}
}
React.render(
<Profile
pic="https://www.diariogol.com/uploads/s1/55/38/91/7/messi-leo-getafe-camp-nou_15_970x597.jpeg" />,
document.getElementById('app')
);
I have used states for height and weight, which can also be used for the zoom effect.
Use:
- Scale animation with Animated Component,
Example:
state = {
zoomImage: 1
}
zoomIn=()=>{
Animated.timing(this.state.zoomImage, {
toValue: 2,
duration: 2000,
userNativeDriver: true
})
}
render():
const animatedStyle = {
transform: [
{
scale: this.state.zoomImage
}
]
}
<TouchableOpacity onPress={() => this.zoomIn}>
<Image style={animatedStyle}/>
</TouchableOpacity>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
Refer: https://medium.com/react-native-training/react-native-animations-using-the-animated-api-ebe8e0669fae

React infinite scroll- double scrollbars showing- I only want the window scroll bar

Im trying to implement infinite scroll for React.js. Everything works fine, except I want to be able to use the window scrollbar, to activate the infinite scroll. The code at the moment, has 2 scrollbars( I only want one).
The code is from stackoverflow answered here, I read his complete answer, I tried setting the height to 100%, but it makes the infinite scroll not work. : Stackoverflow- answered by Aniket Jha, ( the longest answer with 4 upvotes)
Double scroll happens when I render this in this way:
<div>
<First />
<div ref="iScroll" style={{ height: "100vh", overflow: "auto"}}>
<ul>
{this.displayItems()}
</ul>
{this.state.loadingState ? <p className="loading"> loading More
Items..</p> : ""}
</div>
</div>
I have a Link to Codepen if this helps
class Layout extends React.Component {
constructor(props) {
super(props);
this.state = {
items: 30,
loadingState: false
};
}
componentDidMount() {
this.refs.iScroll.addEventListener("scroll", () => {
if (this.refs.iScroll.scrollTop + this.refs.iScroll.clientHeight >= this.refs.iScroll.scrollHeight - 20){
this.loadMoreItems();
}
});
}
displayItems() {
var items = [];
for (var i = 0; i < this.state.items; i++) {
items.push(<li key={i}>Item {i}</li>);
}
return items;
}
loadMoreItems() {
if(this.state.loadingState){
return;
}
this.setState({ loadingState: true });
setTimeout(() => {
this.setState({ items: this.state.items + 10, loadingState: false });
}, 1000);
}
render() {
return (<div>
<First />
<div ref="iScroll" style={{ height: "100vh", overflow: "auto"}}>
<ul>
{this.displayItems()}
</ul>
{this.state.loadingState ? <p className="loading"> loading More Items..</p> : ""}
</div>
</div>
);
}
}
class First extends React.Component {
constructor(props) {
super(props);
this.state = {
};
}
render() {
return (
<h1>Testing</h1>
)
}
}
ReactDOM.render(<Layout />, document.getElementById('example'));
If you don't want the second scrollbar to appear, you need to style the title and sibling div so that they fit in the available viewport.
In your codepen, you have height: '100%' for your scrolling div. This styles the div so that it doesn't need to scroll and infinite scroll therefore doesn't work.
If you style that div so that it takes up less than the height of the available viewport, and render enough items to fill it up, infinite scroll will work fine.
If you then style the title div combination so that they fit exactly into the available viewport space, you won't get a second scrollbar.
Below is an option you have to do this. What I've done is set the height of the scrolling div to be the viewport height (100vh) minus 100px. That's not precisely calculated, but what you want is to subtract the space required for the title from the size of the viewport.
This implementation works fine for me, and should for you as well.
class Layout extends React.Component {
constructor(props) {
super(props);
this.state = {
items: 30,
loadingState: false
};
}
componentDidMount() {
this.refs.iScroll.addEventListener("scroll", () => {
if (this.refs.iScroll.scrollTop + this.refs.iScroll.clientHeight >= this.refs.iScroll.scrollHeight - 20){
this.loadMoreItems();
}
});
}
displayItems() {
var items = [];
for (var i = 0; i < this.state.items; i++) {
items.push(<li key={i}>Item {i}</li>);
}
return items;
}
loadMoreItems() {
if(this.state.loadingState){
return;
}
this.setState({ loadingState: true });
setTimeout(() => {
this.setState({ items: this.state.items + 10, loadingState: false });
}, 1000);
}
render() {
return (<div>
<First />
<div ref="iScroll" style={{ height: "calc(100vh - 100px)", overflow: "auto"}}>
<ul>
{this.displayItems()}
</ul>
{this.state.loadingState ? <p className="loading"> loading More Items..</p> : ""}
</div>
</div>
);
}
}
class First extends React.Component {
constructor(props) {
super(props);
this.state = {
};
}
render() {
return (
<h1>Testing</h1>
)
}
}
ReactDOM.render(<Layout />, document.getElementById('example'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="example"></div>

Input validation setTimeout in ReactJS

I have a component that renders with className="error" or classname="" depending on whether the input is valid or not. This way in CSS I can simply do .error { background: red; }. The validity of the input is determined by the isValidNumber(..) function. However, right now the problem I'm having is that the validation is too instantaneous. If the input is invalid it almost instantly renders with "error" class name which is an annoying UX issue. I would like to have a delay of some sort to not have the class be "error" so instantly, like maybe 0.5 seconds would be nice.
Demo of component. Input is valid on things like "2.3 billion", or "1 trillion", or "203239123", but not "2 sheeps" or "mountain". Github Repo
Here is my component so far. You can see that I tried using setTimeout with setState({ isValid: isValid }) as the function whenever the isValid is false.
export default class NumberInput extends React.Component {
constructor(props) {
super(props);
this.state = {
value: "",
isValid: false
};
}
setIsValid(isValid) {
this.setState({ isValid: isValid })
}
handleChange(event) {
var value = event.target.value
this.setState({ value: event.target.value })
var isValid = isValidNumber(value)
if (isValid === false) {
setTimeout(this.setIsValid(isValid), 2000);
} else {
this.setIsValid(isValid)
}
}
getClassName() {
var className = ''
var errorClass = ''
// Generate error classes based on input validity.
if (this.state.isValid) {
errorClass = ''
} else {
errorClass = 'error'
}
className = 'number-input ' + errorClass
return className
}
render() {
return (
<div>
<input type="text" className={this.getClassName()} value={this.state.value} onChange={this.handleChange.bind(this)} placeholder="Enter a number"/>
<RawNumber isValid={this.state.isValid} value={this.state.value} />
</div>
)
}
}
You need to fix the following line of code:
if (isValid === false) {
setTimeout(this.setIsValid(isValid), 2000);
}
What you are doing here, you are basically calling this.setIsValid instantly and passing to setTimeout the result of it. So the state is changed instantly.
What you WANT to do, you want to pass to setTimeout the function itself, not the result. To do it, you want to wrap this.setIsValid into the function wrapper, like this:
if (isValid === false) {
setTimeout((function() {
this.setIsValid(isValid);
}).bind(this), 2000);
}
I would just add a transition for the background-color property to the .error class.
.error {
background-color: red;
transition: background-color .5s ease;
}
If you want to delay the transition, you can just tack on a value to the end of the declaration. The following would delay the transition for 1 second:
.error {
background-color: red;
transition: background-color .5s ease 1s;
}
I just reread your question. If you want to do the delay in JS as well or instead of CSS, then you need to change your handleChange method to the following.
handleChange(event) {
var value = event.target.value
this.setState({ value: event.target.value })
var isValid = isValidNumber(value)
if (isValid === false) {
setTimeout(this.setIsValid.bind(this, isValid), 2000);
} else {
this.setIsValid(isValid)
}
}

Categories