- Read Tutorial
- Watch Guide Video
What I'd like to do is go to the design. We see that we have an adjective and celebrity one, and what we need to do is map those over. Let's go in here, and one thing I want to do is not say celebrity one on the label. I want to say celebrity because we're just saying adjective here, and not saying adjective one here. Same with nouns. Let's do it with celebrities as well.
Let's go to our code and hit command + b
to close this left thing here. That's in Visual Studio code. Then up at the top here I'm going to say:
card.js
constructor() { super() this.state = { color: ''. pluralNoun: '', adjectiveOne: '', celebOne: '' } this.handleInputChange = this.handleInputChange.bind(this) }
We have those two fields now let's put them in down here:
card.js
render() { return ( <div className="card"> <h1>{this.state.color}</h1> { Input('Color', this.state.color, this.handleInputChange, 'color') } { Input('Plural Noun', this.state.pluralNoun, this.handleInputChange, 'pluralNoun') } { Input('Adjective', this.state.adjective, this.handleInputChange, 'adjectiveOne') } { Input('Celebrity', this.state.celebOne, this.handleInputChange, 'pluralNoun') } </div> ) }
We are going to write it in a more efficient way, but I'm even talking about something else. I'm saying with this having the name and then the state. I feel like there's got to be a more efficient way that I haven't yet come across, but if I ever do I'll throw in an update. Showing us how to do that. Anyway, we'll see if this works.
You'll see that we have all these different unique values in our state now, which is really great.
What we want to do is first get rid of this h1
. I thought we already did.
card.js
render() { return ( <div className="card"> { Input('Color', this.state.color, this.handleInputChange, 'color') } { Input('Plural Noun', this.state.pluralNoun, this.handleInputChange, 'pluralNoun') } { Input('Adjective', this.state.adjective, this.handleInputChange, 'adjectiveOne') } { Input('Celebrity', this.state.celebOne, this.handleInputChange, 'celebOne') } </div> ) }
Then what we'd like to do is talk about a different way we can do this. I guess this isn't that bad of a way. It's probably going to be just as verbose just as long. What we can do to get the rest of these inputs on here is what we'll do right now.
We have all these inputs, let's not worry about what they're called right now, but let's do this and then add in those inputs. So what we need to do is: in our render function let's just make a variable or a constant. I'm going to say:
card.js
render() { const inputData = [ {title: 'Color', state: this.state.color, name: 'color'}, {title: 'Plural Noun', state: this.state.pluralNoun, name: 'pluralNoun'}, {title: 'Adjective', state: this.state.adjective, name: 'adjectiveOne'}, {title: 'Celebrity', state: this.state.celebOne, name: 'celebOne'}, ] return ( <div className="card"> { Input('Color', this.state.color, this.handleInputChange, 'color') } { Input('Plural Noun', this.state.pluralNoun, this.handleInputChange, 'pluralNoun') } { Input('Adjective', this.state.adjective, this.handleInputChange, 'adjectiveOne') } { Input('Celebrity', this.state.celebOne, this.handleInputChange, 'celebOne') } </div> ) }
Now with this data, we need to do something with it because if we do we delete all this, and I'm just going to comment it out, just in case we want to use it again.
If we delete all this, and we go to our app: you'll see we have nothing. That's because we're not rendering anything. So what we need to do is somehow render all this data, and you can't just put this in here to render it because it's a variable. It's exactly like putting celebrity
in here. It is not going to do anything or title for that example. It's not going to get the title. It's not going to do anything.
What we can do is we can use a mapping function in JavaScript, map over it, and then render it. That's probably a little bit confusing, but I'll show you how we can do that. Let's put some brackets here. Let's say this:
card.js
render() { const inputData = [ {title: 'Color', state: this.state.color, name: 'color'}, {title: 'Plural Noun', state: this.state.pluralNoun, name: 'pluralNoun'}, {title: 'Adjective', state: this.state.adjective, name: 'adjectiveOne'}, {title: 'Celebrity', state: this.state.celebOne, name: 'celebOne'}, ] return ( <div className="card"> { inputData.map(data => Input(data.title, data.state, this.handleInputChange, data.name)) } { /* Input('Color', this.state.color, this.handleInputChange, 'color') } { Input('Plural Noun', this.state.pluralNoun, this.handleInputChange, 'pluralNoun') } { Input('Adjective', this.state.adjective, this.handleInputChange, 'adjectiveOne') } { Input('Celebrity', this.state.celebOne, this.handleInputChange, 'celebOne') */} </div> ) }
Now what we're doing is basically the same thing, and it's almost the exact same syntax where we get the same result.
The reason this would be powerful is in a case that wasn't really like this. If we had a form that we had no idea, like we were like: "Okay, we don't know how many plural nouns or adjectives there's going to be. We're going to get that from the user or something." So we have no idea how many items are going to be in the page.
We might pull it from a server, but then if we have input data can map over all of them and render them all. We don't have to type this out for every single input. That way we can have a bunch of different inputs, without initially knowing the amount, and this data would be received from somewhere else.
What we want to do now is: let's see if we are rendering all these over. You notice we have this error that says: Each child in an array or iterator should have a unique "key" prop.
The way we can fix that is pretty simple: by simply passing in a key in here and then setting it.
I don't want to do that yet. We'll do this on a different object, so let's copy this entire thing.
Then, instead of rendering an input, let's render just a div, and comment out the input. What I want to do is put this on a new line so I'm going to put in some curly braces, and say:
card.js
return ( <div className="card"> { // inputData.map(data => Input(data.title, data.state, this.handleInputChange, data.name)) } { inputData.map(data => { return <div>{data.title}</div> }) } { /* Input('Color', this.state.color, this.handleInputChange, 'color') } { Input('Plural Noun', this.state.pluralNoun, this.handleInputChange, 'pluralNoun') } { Input('Adjective', this.state.adjective, this.handleInputChange, 'adjectiveOne') } { Input('Celebrity', this.state.celebOne, this.handleInputChange, 'celebOne') */} </div> ) }
What you'll see is that it's rendering these out but we're still getting the error. A simple way to fix that is by providing a unique value in this div which you could easily do by saying:
card.js
{ inputData.map(data => { return <div key={Math.random()}>{data.title}</div> }) }
That's going to be a random number. I'm pretty sure it's unique almost every time. We get rid of that error, but a better way to do this is to say:
card.js
{ inputData.map((data, index) => { return <div key={index}>{data.title}</div> }) }
We know that the index is going to be unique. It's going to go 0 1 2 3 and so on. So it gets rid of that error, but how do we do that on here. It'd be pretty simple. We could just pass it in.
Here's index, and then go into our input component and fix that and put it in the key. Put in another variable and put a key on the div, or we can map over it a different way and this will fix another problem. Better to write it better.
Let's get rid of these comments here and those divs that we were putting in, so that we just have the input data. Now what we can do is instead of saying all of this in here we could just push the entire object.
Let's take this all the way to here, and I'm going to command + x
to get this data so we can keep it on our clipboard. Now, in here let's just pass in data
.
card.js
{
// inputData.map(data => Input(data))
}
Now let's go into our input.js, and we have four parameters, but we don't want four parameters. We want one; otherwise, we will get a lot of errors. We can do a couple of things. We can either get rid of it and put in one object, and then put it in data, and put: data.title, data.name, data.state.
We can also do this and it fixes it automatically: put in a bracket like it's an entire object and this is our object. This is exactly like passing in just data, except for the difference is it's expanded for us. It's already taken apart so we can use these individual pieces of data. So that kind of that makes sense.
input.js
const Input = ({title, state, onChange, name}) => { return ( <div className="input"> <input name={name} value={state} onChange={onChange}/> <label>{title}</label> </div> ) }
Now what we can do is go back here, but I guess it's not working right now. Let's see why. Okay, so the reason it's not working is because it's not taking in this. If we put that in the object it'll work, but again I don't want to do that. What we can do is:
input.js
const Input = ({title, state, name}, onChange) => { return ( <div className="input"> <input name={name} value={state} onChange={onChange}/> <label>{title}</label> </div> ) }
Then we can reference it like that. This means we'll still have to pass it in, so right here in card.js:
card.js
{
// inputData.map(data => Input( (data), this.handleInputChange )
}
This should work now. Yeah, that works. Now to get that key to work. All I have to do is pass in a key, or what we can do is we could go in input and we could do the math.random thing, or we could just say:
input.js
const Input = ({title, state, name}, onChange) => { return ( <div key={state} className="input"> <input name={name} value={state} onChange={onChange}/> <label>{title}</label> </div> ) }
We could just say state because we know that this piece of state is going to be unique because it's only mapping over each specific piece of state. So I guess it's saying that they have two children with the same key
.
That shouldn't be the case, because these are clearly unique pieces of state. They're all different. So let's go back to our input, and that's really interesting how that's working. Let's put name
and if that doesn't work we'll put `math.random.
input.js
const Input = ({title, state, name}, onChange) => { return ( <div key={name} className="input"> <input name={name} value={state} onChange={onChange}/> <label>{title}</label> </div> ) }
That works because all these names are pretty unique. When I'm saying name, I'm talking about this. these are clearly going to be duplicated. We're going to have more than one adjective, but the name is going to be adjectiveTwo because we need to reference that uniquely, and then use that as a key. Let's get rid of this and now we can type in the rest of our data here.
So what I'd like you to do between now and the next guide is get that data in. I'll record myself right now doing it, but it might take a bit and I'm not going to talk much through it because it's going to take a second.
You're probably going to watch along anyway, but let's get this data in, and feel free to move onto the next guide. I'm just going to put in the data.
card.js
constructor() { super() this.state = { color: '', pluralNoun: '', adjectiveOne: '', celebOne: '', adjectiveTwo: '', nounOne: '', numberOne: '', numberTwo: '', nounTwo: '', adjectiveThree: '', celebTwo: '', celebThree: '', adjectiveFour: '', nounThree: '', celebFour: '', adjectiveFive: '' } this.handleInputChange = this.handleInputChange.bind(this) }
card.js
render() { const inputData = [ {title: 'Color', state: this.state.color, name: 'color'}, {title: 'Plural Noun', state: this.state.pluralNoun, name: 'pluralNoun'}, {title: 'Adjective', state: this.state.adjectiveOne, name: 'adjectiveOne'}, {title: 'Celebrity', state: this.state.celebOne, name: 'celebOne'}, {title: 'Adjective', state: this.state.adjectiveTwo, name: 'adjectiveTwo'}, {title: 'Noun', state: this.state.nounOne, name: 'nounOne'}, {title: 'Number', state: this.state.numberOne, name: 'numberOne'}, {title: 'Number', state: this.state.numberTwo, name: 'numberTwo'}, {title: 'Noun', state: this.state.nounTwo, name: 'nounTwo'}, {title: 'Adjective', state: this.state.adjectiveThree, name: 'adjectiveThree'}, {title: 'Celebrity', state: this.state.celebTwo, name: 'celebTwo'}, {title: 'Celebrity', state: this.state.celebThree, name: 'celebThree'}, {title: 'Adjective', state: this.state.adjectiveFour, name: 'adjectiveFour'}, {title: 'Noun', state: this.state.nounThree, name: 'nounThree'}, {title: 'Celebrity', state: this.state.celebFour, name: 'celebFour'}, {title: 'Adjective', state: this.state.adjectiveFive, name: 'adjectiveFive'} ] }
That only took a few years. So save that, reload our page, and everything's good.
I'm surprised we didn't miss something. Maybe we did. I can already tell that we missed something because we typed into adjective and clearly nothing mapped over. Maybe it did. We have a
in there right now. Okay, so everything's working properly.
Let's go ahead and end the guide right here. Now that we got all of our inputs in. In the next guide, we're going to have to develop a button. What we'll do is we'll develop the content component, so we can get the story over and that you see it here, and then after we get this working we will throw in the buttons and do all the styling.
Let's go here, open a console, and let's say git status
, git add .
, and I'm going to say git commit -m "added remaining inputs"
. We did more, but I don't remember the specifics. I'm going to push. So git push origin master
. Feel free to push if you want. I'll see you in the next guide.