Random Quote Machine - fade in and out animation? How To in React

Hi, im doing the random quote machine project from fcc. I use React with create-react-app. I’ve menaged to build main functionality alone. Howewer i love the fade in animations in the example project.
My App only changes to random color with no animation, by using this code:

state = {
    quotes: [],
    randomQuote: "",
    author: "",
    colors: [
      "#16a085",
      "#27ae60",
      "#2c3e50",
      "#f39c12",
      "#e74c3c",
      "#9b59b6",
      "#FB6964",
      "#342224",
      "#472E32",
      "#BDBB99",
      "#77B1A9",
      "#73A857"
    ]
  };

setColor = () => {
    const newColor = this.state.colors[
      Math.floor(Math.random() * this.state.colors.length)
    ];
    let appNode = document.getElementById("body");
    appNode.style.backgroundColor = newColor;
    appNode.style.color = newColor;
  };

Basically i keep all the colors i want in the state and then use setColor when user clicks New Quote button.
The way animations were done in example project: https://codepen.io/freeCodeCamp/pen/qRZeGZ?editors=0010
is by using jQuery, but i’ve heard that using jQuery with React is not the best practice.
What is the best REACT way to do these animations?

In whatever has the top level html component, so probably App, state something like:

state = {
  currentColor: 0,
  colors: [
      "#16a085",
      "#27ae60",
      "#2c3e50",
      "#f39c12",
      "#e74c3c",
      "#9b59b6",
      "#FB6964",
      "#342224",
      "#472E32",
      "#BDBB99",
      "#77B1A9",
      "#73A857"
    ]
}

Plus a function to change the colour. This you pass down via props to the component that needs it:

changeColor = () => setState({
color: Math.floor(Math.random() * this.state.colors.length)
});

And in the render method, this is the outermost element in your app:

<div style={ backgroundColor: this.state.colors[this.state.currentColor] }>
  {/* Rest of your app */}
</div>

Then in the component

doStuffHandler = () => {
  // Do stuff to do with new quote, and:
  this.props.changeColor();
};

And in the render:

<button onClick={doStuffHandler} />
1 Like

Thank you for answer, yeah, that way is definitely more readable and more React way.
However my main question is how to do the color change animations? I want to fluidly translate from one color to another

It should be something like transition: background-color 2s; in the CSS for that outermost element.

1 Like

One more question. How do i apply this animation. I made it in css:

@keyframes example {
  0% {
    opacity: 1;
  }
  50% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}

.quote-box {
  animation-name: example;
  animation-duration: 2s;
}

And i want to apply it every time user clicks a button using onClick, however it appers only once after page refresh?

So the trade-off you get with React is that it makes it really easy to conditionally add/remove components and rerender them when they change. React will just blow away the things it needs to change and rerender them fresh. But this conflicts with animation, where you transition from one state to another – you don’t remove a thing completely, you just mutate it over time. Some types of animation are hard in React, because it causes a situation where there isn’t anything to animate to, because the thing you want to animate doesn’t exist any more.

So what you want maybe is for the component to fade to 0 when it unmounts, and from 0 when mounts, for which you can use CSSTransitionGroup:

There are more robust solutions which will make things easier, for example this is very good:

Would using the doStuffHandler be the same as using componentDidMount()?

In what component would the doStuffHandler go? I have my app split into three components (App, Quotes, and Buttons). Would it go in Buttons?

No, it’s literally just a function that runs when you click something. componentDidMount is called when the component first mounts. If you wanted to run the function when the component first mounts then, sure, put it there.

So I’m running it in componentDidMount(located in the App component) but I’m now running into a new problem: I already have another prop (handleNewQuote) passed into onClick (found in a button element in the Buttons component). I’ve been trying to find a way to run both this.props.handleNewQuote and this.props.changeColor but haven’t had any luck. This wouldn’t work:

<button type="button" id="new-quote" onClick={(event)=>{this.props.handleNewQuote(); this.props.changeColor(); }}>New Quote
        </button>

What could I be doing wrong, here?

I don’t know what the other thing does or how your app works so not sure. If you’re calling changeColor in CDM, and if handleNewQuote triggers a rerender (ie causes a change to the props/state of the component), then that will in turn trigger the changeColor in CDM because the component will be mounted again so could be that

So what I’m trying to do is incorporate the color change into my generateQuote() function (which is bound to the App component and run in the CDM function). Then, this.generateQuote is passed into the handleNewQuote property. So, in theory, generateQuote should be changing:

  • the quote text
  • the author’s name
  • the author’s picture
  • the wikipedia link attributed to that author
  • the component’s background color (which randomly selected from an array of colors)

For more context:

const quotes = [
  {
    quote: "Many hands make light work.",
    author: "John Heywood",
    profile: "http://www.luminarium.org/renlit/heywoodport.gif",
    link: "https://en.wikipedia.org/wiki/John_Heywood"
  },
  {
    quote: 'One person can make a difference, and everyone should try.',
    author: 'John F. Kennedy',
    profile: 'http://images.politico.com/global/080408_50bar_jfk.jpg', 
    link: "https://en.wikipedia.org/wiki/John_F._Kennedy"
    
  },
  {
    quote: 'The best way to predict the future is to invent it',
    author: 'Alan Kay',
    profile: 'https://qph.fs.quoracdn.net/main-thumb-117344100-200-bevjprxhfxjthnblpqybhtlunpnkhvxy.jpeg',
    link: "https://en.wikipedia.org/wiki/Alan_Kay"
  },
  {
    quote: 'All we have to decide is what to do with the time that is given to us',
    author: 'J.R.R. Tolkien',
    profile: 'https://static9planetadelibroscom.cdnstatics.com/usuaris/autores/fotos/19/tam_1/000018687_1_Tolkien_J_201512041814._R_201512041814._R_201512041814.__200_201512041814.jpg',
    link: "https://en.wikipedia.org/wiki/J._R._R._Tolkien"
  },
  {
    quote: "Everything around you that you call life was made up by people that were no smarter than you and you can change it, you can influence it, you can build your own things that other people can use.", 
    author: "Steve Jobs",
    profile: "https://www.gannett-cdn.com/-mm-/f4f8cf8c812e00141458a67f4c0a3f8af56ca2d4/c=181-0-1079-898/local/-/media/2015/09/01/USATODAY/USATODAY/635766651669578501-SteveJobs.jpg?width=200&height=200&fit=crop",
    link: "https://en.wikipedia.org/wiki/Steve_Jobs",
    background: "https://cdn.pixabay.com/photo/2016/07/06/13/21/office-1500461_1280.jpg"
  }
];
const colors = [
        "#16a085",
        "#27ae60",
        "#2c3e50",
        "#f39c12",
        "#e74c3c",
        "#9b59b6",
        "#FB6964",
        "#342224",
        "#472E32",
        "#BDBB99",
        "#77B1A9",
        "#73A857"
      ]

class App extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      quote: '',
      author: '',
      profile: '',
      link: '',
      currentColor: 0,
    }
    this.generateQuote = this.generateQuote.bind(this);
  }

  
  generateQuote() {
    let randomIndex = (Math.floor(Math.random() * Object.keys(quotes).length))
    let randomColor = (Math.floor(Math.random() * Object.keys(colors).length))
    
    let quote = quotes[randomIndex].quote;
    let author = quotes[randomIndex].author;
    let profile = quotes[randomIndex].profile;
    let link = quotes[randomIndex].link;
    let currentColor = colors[randomColor].currentColor;
    
    this.setState({
      quote: quote,
      author: author,
      profile: profile,
      link: link,
      currentColor: color,
    });
  };
  
  componentDidMount(){
    this.generateQuote();
  };
  
  render(){
    return(
      
        <div id="quote-box" style={{ backgroundColor: this.state.colors[this.state.currentColor] }}> 

          <a href={this.state.link} target="_blank"><img id="profile" src={this.state.profile}/></a>
          <Quote quote={this.state.quote} author={this.state.author} link={this.state.link}/>
          <Buttons handleNewQuote={this.generateQuote} quote={this.state.quote} author={this.state.author} />
        </div>
    )
  };
};

class Quote extends React.Component{
  constructor(props){
    super(props) 
  }
  render(){
    return(
      <div id="wrapper">
        <q id="text" className="quote_text">{this.props.quote}</q>
        <h3 id="author" className="author_text">{this.props.author}</h3>
      </div>
    )
  }
};



class Buttons extends React.Component{
  constructor(props){
    super(props);
  }
  render(){
    return(
      <div className="buttons">
        <button type="button" id="new-quote" onClick={this.props.handleNewQuote}>New Quote
        </button>
        <a id="tweet-quote" target="_blank" href={`https://twitter.com/intent/tweet/?text=${this.props.quote} - ${this.props.author}`}><button>Tweet This!</button></a>
      </div>
    )
  }
  
  
};

ReactDOM.render(
  <App />,
  document.getElementById('content')
)

Oh wait! Nevermind! I figured it out! Yay!

how? I’m going crazy on calling the change quote and change color together…

I have no idea what your code looks like so it isn’t possible for me to answer that.