Quote Machine Project using React

I can imagine different ways to do this. You could just have an array stored in redux and then a reducer to set the current index to use. You could have the array of quotes local to your React code and you’re storing the quote in redux. Really, you can easily do this without redux, but I understand if you want to practice it.

If this were a real world app, I would probably get the quotes with an API call - either as a whole or one at a time.

To simulate the first, I would just have an array as the initialState for that reducer, something like “quotes”. IRL this would be gotten asynchronously and stored, but here it’s hard coded. Of course you could also just hard code it in your React code and get the same effect. And then you’d have a reducer like “quoteIndex”.

For the second, you would have to deal with a lot of asynchronous API calls. For something this simple, you can handle that in your React. For something IRL, you’d need something like redux-thunk or redux-saga.

But there is more than one way to do it. You should pick one that makes sense to you. Don’t be afraid to make mistakes.

1 Like

Hi there,

I just placed the array at the begining of my React Code and then, in order to select an index from the array I researched how to use Math.random() in a way that it does not select the same quote two or more times in a row (which is the default behaviour of the method)

Here’s a link to that research on stackoverflow:
generate a random number not equal to the last random number generated

You might find this useful. It certainly opened my eyes to the way that you can use a function as an object and add / read properties to Math.random

Hope this is of some use to you.
LT

PS: I can drop you a link to my final solution of the Random Quote Machine if you like. Just drop me a line if you do.

1 Like

thanks for the hint , me either i didn’t knew that in javascript a function is an object too … :bulb:

Yes, in JS, everything that isn’t a primitive is an object, that includes objects, arrays, functions, classes, etc.

I would be very careful about adding properties to functions though. This can cause confusion as people don’t normally expect to look for props on functions. Yes, it’s possible, but we like our code to be intuitive as possible.

so i did some progress but right now i am stuck , i don’t know why it is not working ? :thinking:
also i was wondering why instead of using Math.random we are not able to use a variable that we will increment each time that we call the reducer ??

I’m having a hard time understanding what you are trying to do.

First of all, I don’t see how you are trying to connect your React to your DOM. For your HTML, this is all I’m expecting to see:

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

And then somewhere at the bottom of your JS you’ll have something like this:

ReactDOM.render(<App />, document.getElementById("root"));

And then you have a root React component called App and everything spreads like a tree from there. All of your HTMl gets built as JSX. Right now it just looks like you have fragments of React code and some unconnected HTML. So, it doesn’t matter what you do in your React, it won’t affect the DOM.

Don’t even think about anything else until you get that straightened out.

And then I would get it working without Redux. You are trying to do too many things at once that you haven’t quite yet grasped.

A word on this:

const loadReducer = (state = " ", action) => {
  const randNum = () => {
    return (randNum.x = Math.floor(Math.random() * (quotes.length))) === randNum.y ? randNum() : randNum.y = randNum.x;    
  }
  switch(action.type) {
    case Load:
      return state = action.quotes[randNum];
    default: 
      return state;
  }
};

OK, after some formatting (please format your code as you write it) This is basically a sound reducer in that the mechanic are sound, even if it isn’t doing what you want.

The first thing is that this:

  const randNum = () => {
    return (randNum.x = Math.floor(Math.random() * (quotes.length))) === randNum.y ? randNum() : randNum.y = randNum.x;    
  }

is too complicated. You have recursion inside a ternary. A ternary that is doing internal assignments. And assigning properties to a function. Yes, I get what you are going for, but there are cleaner ways to do the same thing. Why not start out with this:

  const randNum = () => Math.floor(Math.random() * quotes.length); 

for now. When you’re struggling with something, simplify, don’t make it more complex.

The other problem is here:

      return state = action.quotes[randNum];

You are using randNum as the index. But what data type is randNum? This is why I like to start function names with action verbs so I don’t get confused. If I read your variable name, I would assume it is a number.

But again, simplify. Just see if you can get a basic React app going. Get your button up. Then get your button to fire your function. Then get your random quote to appear. Each of those is at least a 10 step process. Don’t even think about Redux before you have it working without Redux. Make small changes and test.

Hi,

so i followed your advice and start to develope a small app with react only, instead of random function i used a counter, it seems working until now , i started to fulfill the user story and check if it passed the test, at the moment i can not go beyond the first test , it make me asking my self questions on how the rendering process work , cause most of the stories ask you to create id attribute , i tried to add them in both the html code and the jsx , did not work after that i tried to put it just on one side to try to understand what is happing when the rendering process start but it keep showing that the test failed ?? looking for enlightenmend :compass:

NOOOOOO!!! Don’t add HTML. Bad dev. Don’t touch the HTML. Other than what I showed above, the HTML shouldn’t have anything in it and everything is done with the JSX. Yes, you can get it to work with a hybrid method, but it gains you nothing and it hurts your React education.

As to the first test:

  1. I can see a wrapper element with a corresponding id=“quote-box”.
    expected null to not equal null
    AssertionError: expected null to not equal null

So, it’s telling you that the wrapper should have that id. In your code:

 <div id = "wrapper" class="main"> 
   <div id="quote-box">

If I combine those (giving that outer wrapper the id of “quote-box”, it passes the test. Really, you have an extra div here and the id is in the wrong place. (And this is in the HTML and not the JSX.)

2 Likes

i did some progress everything seems to work well until i started the design with bootstrap , i just use classes to put some colors in my buttons and now dont know why when i click on new quote nothings happen ??

              <button  id = "new-quote" onClick = {this.reset} class="btn btn-primary fa-paper-plane">

I don’t think that’s the method you want to call.

1 Like

Also, I find your

    if(this.state.count > 3)

Odd. If you want to index through the quotes, then I would just put logic in your handleClick to make sure that if you get to the last index, you start over at 0. Of course, this should be replaced eventually anyway because the assignment was to make a random quote generator, not a deterministic quote generator. But I understand if you want to do this as a “let me take care of one problem at a time” philosophy - a great approach to coding.

But now I see why you have two branches - you’re trying to send it to different click functions. But you could have done that more simply like:

<button
  id = "new-quote"
  onClick = { this.state.count > 3 ? this.reset : this.handleClick }
  class="btn btn-primary fa-paper-plane"
>
1 Like

copy and past was the issue on this one , i admit it , and yeah about the way, you were right using react and JSX is more simpler than any other way …

Oh yeah, copy and paste errors, we all know them well. But again, you shouldn’t be duplicating all that JSX anyway.

after all i prefer the code that i have up front of me right now than the old one , it looks cleaner , i assume that the expression clean code came from this idea that you have to always keep it simple , avoid complexity as much as possible , but still when i teste it i find that sometimes when i click i needed to do it twice for the quote to appears (used random method ) ? :thinking:

Yeah! That really annoyed me too and hence why I posted my research on that specific problem. However, the wise words of @kevinSmith should be taken on board:

Un-intuitive can be deceptive . :innocent: .

I just wonder how @kevinSmith would handle this problem in the easiest possible way?

I am just a novice too!

1 Like

Yeah, that’s the generic idea. There’s also a popular book called Clean Code by Martin.

I just wonder how @kevinSmith would handle this problem in the easiest possible way?

I assume you’re talking about the random number problem? Specifically, how to avoid getting the same number twice in a row.

Again, I would avoid props on functions. And I tend to avoid recursion when I can. It doesn’t do much harm here, but there’s often a simpler, quicker solution. (Don’t get me wrong - there are some problems where recursion is a lot sexier than an iterative solution.)

So, the brute force method would be to have a while loop and just keep getting random numbers until you get one that’s different. That’s basically an iterative version of your recursive solution. It works, but it’s ugly.

I was taking a walk through the woods smoking my pipe after we’d talked about this before. I occurred to me that there is an elegant solution if you need a new (not the same) random value in a range. (In all fairness, it’s undoubtedly occurred to a lot of people before me.) If we have 10 quotes, really we need an offset, a distance to the next index, we need a random number between 0 and 9. For visualization, we currently have a value of x and we want a new random value - anything but x. Imagine these 10 numbers (0 to 9) on a continuous ring. If we move clockwise from x, we will run into all the candidates, first 1 move away, then 2, etc. until we get to 9 moves away. At 10 moves around the circle we get back to x. So, we need a random number (call it y) between 1 and 9 which we can add to x so we can get to a random number that will not be x. Of course, we are not on a circle. But we can add the offset and then use a modulus: (x + y) % 10. I came up with something like this:

const getRandomIndex = (currentIndex, size) => {
  const offset = Math.ceil(Math.random() * (size - 1));
  return (currentIndex + offset) % size;
};

or it could just be:

const getRandomIndex = (currentIndex, size) => (currentIndex + Math.ceil(Math.random() * (size - 1))) % size;

You can mess around with it in a pen here.

1 Like

I sincerely appreciate you sharing your thoughts on this, I hope one day too to be able to think in such a manner.

…and for the record, those pipe moments should never be under-estimated!
Do Not Disturb!

To the extent that I can “think that way”, it’s just practice. Every algorithm you solve rewires your brain just a little.

You are so correct, I think the process of solving a problem makes the mind wander down recent and distant paths, forming new connections.

I need to do this full time, my other work is interfering with it . :pensive:

guys give me your feedback :thinking: