So, last time we managed to create a React application, and put a div under its header, when the user clicks the link. If you want to see how we did this, you can look over here.
I think today I’m going to put some input fields in this div and a button. If it’s too easy also make the form pop up right below the link, rather than below the entire header. Perhaps this is only css. Like last time, fails are expected, but I anticipate they would be fewer. Let’s see about that. Ready? Here we go!
Creating input fields
Well this one would be easy. I just need to put the input fields in the render method of the LoginForm component:
class LoginForm extends React.Component { constructor(props) { super(props); } render() { return ( <div> <input type="text" name="username" placeholder="Username" /> <input type="password" name="password" placeholder="Password" /> </div> ); } }
And we have two input fields on the web page already. That wasn’t too hard. Let’s put the button too! I’ll add this:
<button type="submit" name="login">Log In</button>
Yep, that works! Well, not if you try to click it, it doesn’t. Let’s put some action into it, shall we?
Linking the Button With a Click Handler
Now, let’s use the data from the input fields and show it on the screen, once the button is clicked. Now that would be one hell of a log-in form wouldn’t it? “You think your password is safe, don’t you? Here, take that!” :D. How do you do this though? The holy Internet surely knows how! That’s clearly not how I’ll find out though. Let’s do this:
onSubmit() { console.log("Double bazinga!"); ReactDOM.render( <span>username:{document.getElementById('username').text}</span> <span>password:{document.getElementById("password").text}</span>, document.getElementById("logIn"), ); }
And set this as a click handler to the button. Let’s check it!
…
Of course, there’s an error, but that doesn’t put me down. I think it’s pretty clear saying “Adjacent JSX elements must be wrapped in an enclosing tag. I’ll put a div around the spans. Like so:
ReactDOM.render( <div> <span>username:{document.getElementById('username').text}</span> <span>password:{document.getElementById("password").text}</span> </div>, document.getElementById("logIn"), );
Now the labels show! But my data extraction from the input fields is less than useful. I thought these braces just let you execute javascript expressions, don’t they? Perhaps my javascript is bad (oh, the irony, to work with javascript daily and to fail at it miserably). I need to go back to school.
There, it’s not the “text” property I search for, it’s the “value” property. Anyhow, this is not the way we should be extracting state from the DOM. We should probably not extract any state from the DOM at all. So how should I change this, to comply with React ideology?
Well, as far as I understand the Facebook devs behind this framework, they don’t really like storing information in the DOM. Keeping your state in the DOM means you have to interact with it all the time, and although I think of myself as a young guy, I’m old enough to remember what it is to deal with IE6. And IE6 is the poster child of DOM problems. So let’s be smart, and not touch the DOM at all. Leave that to Facebook, they pay someone for that.
Therefore, I’m going to make a property in the LoginForm component, which would be called “state” and would be a simple object. Further I’m defining a event handler, which would trigger on change of each of the input fields. To create the property we need to define a constructor:
constructor (props) { super(props); this.state = {}; }
There! It’s pretty easy. I don’t like it, because it doesn’t look like my favorite style of javascript, but there you go. We’ll fix it later. And the input fields would look like this:
<input type="text" name="username" id="username" placeholder="Username" onChange={this.change} /> <input type="password" name="password" id="password" placeholder="Password" onChange={this.change} />
If you notice, the change in the input fields is the new argument: “onChange”. There is a slight problem with the input fields now – we don’t have the method yet. What I know about it though, is that it would magically receive a parameter, which I call “event”, but you might call it Steve as well. There we go with it:
change (event) { this.state[event.target.name] = event.target.value; }
Now here Steve, the event, has a target and the target has info about the input field. Basically whatever property the input field has, Steve, the event, knows it. This means, we can ask him about it and use it to store the input state into the component state. And that’s what I’ve done in this method. But when I run this, the page complains, that “change” is not a thing. Why would it do that?
Binding the Event Handler
Ah, of course, the age old javascript problem of dynamic function context. If the method is defined in a certain object, it doesn’t mean it is its exclusive property and no one else can use it. So I’ve passed it as a property to the input field, and this field thinks it is nobody’s method now. And if a method is not owned by anyone, it has no “this” to refer to. Let’s bind those methods:
<input type="text" name="username" id="username" placeholder="Username" onChange={this.change.bind(this)} /> <input type="password" name="password" id="password" placeholder="Password" onChange={this.change.bind(this)} />
There! Done! Now we have a less than useful form, which announces our password to the world!
Do you enjoy these articles? I’ll be shipping more of them soon! The next one would be on this extra thing I was planning for today. Stay tuned and:
Happy coding!