Random Quote Machine - State values not being passed as props

H Folks,

I’m just starting the random quote machine project and could do with a little guidance. I’m working on codepen using html, scss and React.

At the moment I have one stateful component from which I am trying to pass state values down to two stateless components as props:

For now, the state just has default values whilst I get things working. Baby steps!

// ***** Random Quote Machine Start *****
// stateful component
class MyApp extends React.Component {
  constructor(props) {
    super (props);
    this.state = {
      text: 'Our default quote',
      name: 'Default Author'
    }
  }  
  render() {
    return (
      <div>
        {/* pass on state values as props */}
        <CurrentQuote text={this.state.text} />
        <CurrentAuthor name={this.state.name} />
      </div>
    )
  }
}

The two components that I am trying to pass props to:

// stateless component
class CurrentQuote extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return(
      <div>
        {/* this <p> tag is being rendered but props are not?*/}
        <p>.... {this.props.text} ....</p>
      </div>
    )
  }
}

// stateless component
class CurrentAuthor extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return(
      <div>
        {/* this <p> tag is being rendered but props are not?*/}
        <p>Author - {this.props.name}</p>
      </div>
    );
  }
}

Then I call the ReactDOM.render api:

// Render the components to the DOM
ReactDOM.render(<CurrentQuote />, document.getElementById("text"));
ReactDOM.render(<CurrentAuthor />, document.getElementById("author"));

I can see that the tags within the two stateless components are being rendered but the props text and name are not.
image

I must be doing something fundamentally wrong here? Would someone be kind enough to put me on the right tracks.

I know that I can put the entire html of my quote-box in the return statement of my stateful component so that I can access state values directly like so:

// ***** Random Quote Machine Start *****
// stateful component
class MyApp extends React.Component {
  constructor(props) {
    super (props);
    this.state = {
      text: 'Our default quote',
      name: 'Default Author'
    }
  }  
  render() {
    return (
      <div>
        <div id="wrapper">
          <div id="quote-box">
            
            <div id="top-row">
         <i id="quote-icon" class="fa fa-quote-left"> </i><span id="text">{this.state.text}</span>
       </div>
            
            <div id="middle-row">
         <div id="author">Author's Name: {this.state.name}</div>
       </div>
            
            <div id="bottom-row">
         <a  id="tweet-quote" href="twitter.com/intent/tweet" target="_blank"><i class="fa fa-twitter-square"></i></a>
         <button class="button" id="new-quote"><span class="button-txt">New Quote</span></button>
       </div>
           
          </div>
        </div>
      </div>
    )
  }
}

// Render the components to the DOM
ReactDOM.render(<MyApp />, document.getElementById("JSX"));

…and get this result:
image

But that looks like one messy return statement! I’m sure that’s not how it’s supposed to be done which is why I created the other two components and tried to pass state values as props - it just isn’t working though and I’m not sure where I’m going wrong.

Can anyone help please?

The link to the project pen is here: Random Quote Machine Project

Cheers,
LT

Yeah, there are some structural problems here.

Your html should be just

  <div id="JSX"></div>

That’s it. If you want to wrap it in a body, fine. I would point out that “root” or “app” are more common names for this than “JSX”, but that doesn’t matter. You also in this case need the testable-projects thing in there, that’s find. This is the html you need:

<body>
  <div id="JSX"></div>
  <script src="https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js"></script>
</body>

All other html will be created in your React components.

Next, don’t do this:

ReactDOM.render(<CurrentQuote />, document.getElementById("text"));
ReactDOM.render(<CurrentAuthor />, document.getElementById("author"));

You should only hook in with your root component:

ReactDOM.render(<MyApp />, document.getElementById("JSX"));

If I do that, then I start to see the data (behind the test hamburger). You need to rebuild the buttons as React components, but at least things start to work at that point.

1 Like

@kevinSmith
Thank you so much Kevin. That’s exactly what I did here: Random Quote MC Forked

I just thought I was going about it the wrong way doing that. I’m so glad you clarified that for me.

Take care now.

Many thanks
LT

Nope, that looks like the right way to do it. Now just need to figure out how to get those buttons to do what you want and how to get your quotes.

The only thing I would add is that those sections should be broken up in their own subcomponents, kind of like you were doing in the old version. True, this app is small enough that you could get away with one big “god” component, but that’s not really the React way to do it.

I’m struggling with this, when you say ‘sections’ do you mean that my #top-row #middle-row and #bottom-row should be separate components?

I mean more conceptually. As a React developer, I think of the hierarchy as:

MyApp ---|
         |--- Display ----|
         |                |--- Quote
         |                |--- Author
         |  
         |  
         |--- Controls ---|
                          |--- TwitterButton
                          |--- NewQuoteButton

It’s a little overkill for an app this small, and there are other ways to organize it, but that is the basic idea.

I don’t think it’s overkill, I need to start thinking like this! This is what I came up with so far:

class MyApp extends React.Component {
  constructor(props) {
    super (props);
    this.state = {
      text: 'Our default quote',
      name: 'Default Author'
    }
  }  
  render() {
    return (
      <div>
        
        <div id="wrapper">
          <div id="quote-box">
            {/* child components */}
            <CurrentQuote text = {this.state.text} />
            <CurrentAuthor name = {this.state.name} />
            <ButtonBar />            
                       
          </div>
        </div>
      </div>
    )
  }
}

and then I have those defined as separate components

Random Quote Machine ReOrganised

Is this looking better?

Thanks so much for your input, it’s greatly appreciated :slight_smile:

Just remember you will have to pass down the method that sets a new quote as a prop so you can call it from inside the child component.

1 Like

Cheers @lasjorg I was just contemplating what else I would need to pass down as I was commenting my code out!

Now I can start thinking of how to make things work!

I was going to start out with an array of quote’s, make the new quote button generate a random number when clicked then use that number to select the array index. Then set the state values accordingly.

…and then (once we get that working) think about async and stuff!

A little way to go yet :slight_smile:

Cool. That is a slightly different structure than what I was suggesting, but it is also a completely reasonable one and on a different day it might be what I chose.

1 Like

Thank you Kevin,

You have been a great help, I now understand things a little better than before and hopefully can move forward a little easier.

You’re definately on my Buddy List :+1:

Cheers now, speek to you soon.
LT

1 Like

Wow! The learning curve on this stuff is pretty steep man! I’ve been so stuck on the silliest little things:

  • getting the new quote button to work, I was simply trying to pass down onClick rather than handleClick as a prop dope!

  • getting the app to render a random quote on page load, opposed to displaying my default state values (which for the record, made the FCC tests to pass!)

but ‘what don’t kill us makes us stronger’ as they say!

This is where I got to using just React and SCSS:

Random Quote M/c

Thanks again to @kevinSmith for that essential insight into how I should have structured the app (it was pretty broken at the start!) and then @lasjorg - that was a pretty awsome reminder that I kept looking at as I was trying to fix me new quote button!

Cheers guys,
LT

Oh yeah, definitely. React is a very different way of thinking. And then Redux will be a whole other mind twist. But when you get it, it starts to make sense.

Cool, it looks pretty good.

Looking over the code, I might comment:

When you set your initial state, you can just do:

    this.state = {
      text: '',
      name: ''
    };

You don’t need those default values in there.

You’re getQuote can be just:

 getQuote() {
   let x = Math.floor(Math.random() * quotes.length);
   this.setState(quotes[x]);
 }

since your state has the same shape as each quote.

The method handleClick is kind of pointless - you can just pass in getQuote:

            <ButtonBar handleClick={this.getQuote}/>     

I might create a method like handleClick if I had some other things I needed to do besides getQuote, but if that’s all you’re doing, then you don’t really need to.

For any class method, if your constructor is just:

  constructor(props) {
    super(props);
  }

Then you can leave it off - React will just assume that anyway.

But cool, it looks good, have fun on the next project.

Yeah, React is confusing until you wrap your head around it. But it is very powerful and popular.

1 Like

That’s for sure!

Yeah! I purposely left Redux out of the equation - keep my first app simple, it is simple app after all :wink:

I forgot to remove those default values, I could have said that I just left them there in case I broke things in the future - but that would have been a lie!

Now that is an insight, thank you for that.

Point taken! I left it in there to remind my future self of how you can call a method from within a method. Initially, the handleClick method was doing what the getQuote method is doing. Then I used the getQuote method inside componentDidMount to set the initial state and left the modified handleClick method there. I never considered what you suggested. Cheers again!

I was wondering if I needed that there. Thanks, it was really niggling my brain!

Yeah. It looks a great tool. I think I like it a lot!

Thanks again for your fantastic input. You are a true star!
LT

It’s important to celebrate your accomplishments!

1 Like