1

I need to show a component with fade in or fade out and hide it altogether, depending on the property value (true or false).

class Tooltip extends Component {
  constructor(props) {
    super(props);
    this.state = {visible: false};
  }

  componentWillReceiveProps(newProps) {
      if (newProps.status === true) {
      this.setState({visible: newProps.status});
    } else {
      this.setState({visible: false});
    }
} 

  render() {

return (
  <div className={"fade" + ( this.state.visible === true ? ' in' : '' ) + " tooltip tooltip-danger"}>
  <div className="text">{this.props.text}</div>
  </div>
);
    
  }
}

Everything works well, the component appears and disappears smoothly, but is not removed from the output.

And I need to completely remove the component after smoothly hiding.

I try to change the code a little, but in this case the component is removed from the output, but the fade-in and fade-out effects disappear ...

class Tooltip extends Component {
  constructor(props) {
    super(props);
    this.state = {visible: false};
  }

  componentWillReceiveProps(newProps) {
      if (newProps.status === true) {
      this.setState({visible: newProps.status});
    } else {
      this.setState({visible: false});
    }
} 

  render() {

if (this.state.visible === false) {
    return null;
}

    return (
      <div className={"fade" + ( this.state.visible === true ? ' in' : '' ) + " tooltip tooltip-danger"}>
      <div className="text">{this.props.text}</div>
      </div>
    );
        
      }
    }

Please, help me! Thank you!

Vlad
  • 91
  • 5

5 Answers5

0

You can try using &&, for example this way bool && component

in your case so:

render() {

  return ( 
    {this.state.visible && <div className = "text" > </div>}
  );

}

or something like that

0

I'm splitting your problem in these phases:

  1. mount the tooltip component with opacity 0
  2. animate opacity to 1
  3. after user interaction animate opacity back to 0
  4. unmount tooltip component

Since you want to mount / unmount the component and play some css transitions at the same time you need to have a component in the mmiddle that monitors the phases of the animations before unmounting the tooltip (mounting is not an issue). I was facing a similar problem and my solution was with this library: framer-motion

I had to create this component to manage the animation state and the mount state of the components inside:

import { AnimatePresence, motion } from "framer-motion";
import React from "react";

const OpacityAnimation = ({
  className, // optional, in case you need additional classnames
  children,
  duration = 0.3, // duration in seconds
  show // this prop will determine if the component is hidden or shown
}) => (
  <AnimatePresence>
    {show && (
      <motion.div
        style={{ overflow: "hidden", opacity: 0 }} //initial style
        animate={{ opacity: 1 }} // when mounted this style property will be animated
        transition={{ duration }}
        className={className}
        exit={{ opacity: 0 }} // this style will be animated when the component is unmounted
      >
        {children}
      </motion.div>
    )}
  </AnimatePresence>
);

export default OpacityAnimation;

for working example of how to use this component check here

brein
  • 1,409
  • 9
  • 11
  • This partially removes the component, but I need to remove it completely. If you substitute this construction at the very beginning of the output, the effect of appearance and fading disappears again ... – Vlad Jan 08 '21 at 04:33
  • I'm not sure what you mean with `the very beginning of the output`, why don't you create a minimum working example on codesandbox, I will make it work there – brein Jan 08 '21 at 04:37
  • this does not solve the problem - the component can now be removed but there is no fade in return (
    {this.state.visible === true && (
    {this.props.text}
    )}
    );
    – Vlad Jan 08 '21 at 04:39
  • I have edited my answer, please check the example I have linked – brein Jan 08 '21 at 05:15
  • Brein, Thanks, but I would not like to use additional libraries. I would like to optimize my code. You can help? – Vlad Jan 08 '21 at 05:20
  • At the time I did some research and came to the conclusion that making it by myself is not easy and not worth my time since there's plenty of good solutions out there made by people far smarter than me :) framer was the easiest lib to implement in my project. another valid one is this: https://github.com/reactjs/react-transition-group/tree/v1-stable – brein Jan 08 '21 at 06:02
0

Thanks to UJJAVAL SHAH for the tip about timer. I found a solution.

class Tooltip extends Component {
  constructor(props) {
    super(props);
    this.state = {visible: false, fadein: false};
  }

  componentWillReceiveProps(newProps) {
      if (newProps.status === true) {
      this.timer1 = setTimeout(() => this.setState({fadein: true}), 100);
      this.timer2 = setTimeout(() => this.setState({visible: true}), 50);
    } else {
      this.timer1 = setTimeout(() => this.setState({fadein: false}), 100);
      this.timer2 = setTimeout(() => this.setState({visible: false}), 350);
    }
  } 

  componentWillUnmount() {
  this.timer1 = null;
  this.timer2 = null;
  }  

  render() {

    return (
      <div>
      {this.state.visible && (
      <div className={"fade" + (this.state.fadein ? ' in' : '') + " tooltip tooltip-danger"}>
      <div className="text">{this.props.text}</div>
      </div>
      )}
      </div>
    );

  }
}

This works, but I'm not sure if this is the correct solution. Maybe eating more thoughts?

Vlad
  • 91
  • 5
  • you shouldn't be using `componentWilReceiveProps` as it is being deprecated in react 17 and will be removed in future versions.. more info [here](https://stackoverflow.com/questions/51980977/why-is-componentwillreceiveprops-deprecated) And if you want to clear a timeout you do it with `clearTimeout(this.timer1)` – brein Jan 08 '21 at 08:41
  • and another note: I think timers are not the right way of solving this problem.. That's not how you are supposed to use react :( – brein Jan 08 '21 at 08:45
  • What solution can you suggest? I need to completely hide the entire component before fading in and after fading out. How can you do it differently? Hide simply divs with text via way bool && component as others suggest does not suit me, tk. the previous block overlaps other content for me. – Vlad Jan 08 '21 at 16:19
  • I have posted a working example in my answer that is doing what you are asking. Component is mounted with opacity 0 ad then fade in animation is played. When unmounted fade out animation is also played and then it’s removed. No timers, no deprecated methods, that’s how I would do it based on my 3 years experience working with react – brein Jan 08 '21 at 19:49
-1

There are a number of way to conditionally render a component in React.

I have provided a few examples of inline conditionals below:

function Component = () => {
    const conditionA = true;
    const conditionB = false;
    return (
        <div>
            {
                // Conditional render based off a truthy condition.
                conditionA && (
                    <div>
                        I will be rendered will conditionA is true.
                    </div>
                )
            }
            {
                // Conditional render based off a truthy condition.
                conditionB && (
                    <div>
                        I will be rendered will conditionB is true.
                    </div>
                )
            }
            {
                // Conditional if / else using a ternary.
                conditionA ?
                    <div>
                        I will be rendered will conditionA is true.
                    </div> :
                    <div>
                        I will be rendered will conditionA is false.
                    </div>
            }
        </div>
    )
}
Jacob Smit
  • 2,206
  • 1
  • 9
  • 19
  • I know this doesn't cover your "after fading" use case but I thought it might be enough if you already have the fading done. – Jacob Smit Jan 08 '21 at 03:55
  • If i add code - if (this.state.visible === false) { return null; } - i have problem... 'in' added but not fading... – Vlad Jan 08 '21 at 04:02
-1

In componentWillReceiveProps() -- false case, You can use setTimeout().

    else{
            timer = setTimeout(() => this.setState({visible: false});, 3000)
}

But this way you will need tow properties in the state object. One for fade-out class and another for ^^ removing the element (which will have timeout).

  • Unfortunately this does not fit. This tooltip should not fade out in time ... It should fade out depending on the value of the props. – Vlad Jan 08 '21 at 04:19
  • In the state object, you will have two properties. 1. which removes the element. 2. for fade-out purpose. So in the else statement, you will update both the state properties. the (prop-2) will change the class and fade-out. the (prop-1) will have a timeout and will remove the node. BUT YOU HAVE TO UNSET BOTH OF THEM in the `componentWillReceiveProps() else()` – UJJAVAL SHAH Jan 08 '21 at 04:37