Building Random Quote Generator in React

Hello,

I am attempting the Random Quote Generator with React, however I may have structured my components wrong because I have hit a bit of a wall.

I have built the random quote machineall in one component but I want to separate the elements to make it more readable and maintainable.

you can see my structure in my pen here

It renders a random quote on load but I am not sure how to get a new quote upon clicking the ‘new quote’ button because the buttons are rendered in a component and the author/text is in another component.

How can use the functions in the buttons component to interact with elements in the author/text component?

You need to assign a state to them, and change the state when the button is clicked. So example is this:

class App extends React.Component {
  state = {
     quote: '',
     author:''
  }

  handleClick = () => {
      this.setState({
         quote: 'new quote',
         author: 'new author'
      }); //new quote
  }

  render(){
    return(
      <div>
        <blockquote>
          {this.state.quote}
          <footer>{this.state.author}</footer>
        </blockquote>
        <button onClick={this.handleClick}>new quote</button>
      </div>
    );
  }
}

the quote is automatically updated because the state watches for changes. i suggest do the react challenges for more info :slight_smile: or check the official react docs

Happy Coding!!

Thanks, but this is what I have already implemented with everything in one component.

In my code here, the buttons are in a separate component than the quote and author elements

class Text extends React.Component {
  
  constructor(props) {
    super(props);
    this.state = {
      content: '',
      author: ''
    }
  }
  
  componentDidMount(){
    let number = (Math.floor(Math.random() * 5));
    
    let quote = quotes[number].content;
    
    this.setState({content: quote})
    
    let author = quotes[number].author;
    this.setState({author: author})
  }
  
  render() {
    return (
      <div>
      <h1>"{this.state.content}"</h1>
      <h2>- {this.state.author}</h2>
        </div>
    )
  }
}

class Buttons extends React.Component {
  constructor(props) {
    super(props);
  
    this.newQuote = this.newQuote.bind(this);
    
    this.tweetQuote = this.tweetQuote.bind(this);
  }
  
  newQuote() {
    alert("new quote");
  }
  
  tweetQuote() {
    alert("tweet quote");
  }
  
  render() {
    return (
      <div>
        <span className="quote-button" onClick={this.newQuote}>New Quote</span>
        <span className="tweet-button" onClick={this.tweetQuote}>Tweet Quote</span>
      </div>
    )
  }
}

That is just an example code but In order for your Text and Buttons Component to interact, you need to lift the state up to its parent, the Quoter Component and pass the state and methods as props… so its like:

class Quoter extends React.Component {
  state = {
     quote: '',
     author: ''
  }

  newQuote() {
    //your new quote logic here
  }

  render() {
    return (
     <div>
        <Text quote={this.state.quote} author={this.state.author} />
        <Buttons handleNewQuote={this.newQuote} />
     </div>
   );
  }
}

and pass them as props.

1 Like

Nice one Jonathan! i forgot about passing functions to components as props

image

I’m glad you made it. When you feel your question is solved feel free always to mark the post as solved. :smile: thanks!

Hi everyone,
I’m very new to React. I’m trying to build the random quote machine. but instead of hard coding quotes, i used fetch API to get them. My ‘machine’ can get and display a random quote from API when the GetQuote button is clicked. However, I’ve not figured out the way to get a random quote on the first load?!! :frowning:
I don’t know what did i do wrong or my approach was not right and led to dead end?
Could you shed some light on this please?
here is the code

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

class QuoteBox extends React.Component {
  constructor() {
    super();
    this.state = {
      quote: "",
      data: {},
      count:0
    };
    this.clickHandle = this.clickHandle.bind(this);
  }
  componentDidMount() {
    fetch(
      "https://gist.githubusercontent.com/camperbot/5a022b72e96c4c9585c32bf6a75f62d9/raw/e3c6895ce42069f0ee7e991229064f167fe8ccdc/quotes.json"
    )
      .then(response => response.json())
      .then(data => {
        this.setState({
          data: data,
        });
      });    
  }
  clickHandle() {
    const randomIndex = Math.floor(Math.random() * 102);   
    this.setState({
      quote: this.state.data.quotes[randomIndex],
      count: this.state.count+1
    });
  }

 render() {
   
    return (
      <div id="quote-box">
        <Quote
          quote={this.state.quote.quote}
          author={this.state.quote.author}
        />
        <button id="new-quote" onClick={this.clickHandle}>
          {this.state.count===0 ? 'Get Quote':'Next Quote'}
        </button>
      </div>
    );
  }
}

I think you can do it with 1 line :slight_smile:

componentDidMount() {
    fetch(
      "https://gist.githubusercontent.com/camperbot/5a022b72e96c4c9585c32bf6a75f62d9/raw/e3c6895ce42069f0ee7e991229064f167fe8ccdc/quotes.json"
    )
      .then(response => response.json())
      .then(data => {
        this.setState({
          data: data,
        });
      });    
  }

Just after your component mounts it runs this componentDidMount() function automatically right?

However a quote is only shown when the clickHandle() function is called. And at the moment that function is only executed when the button is clicked.

So you need to think of a way to get that function to run just after the component has been mounted and the quotes have been fetched from the API.