Previously, we said that the state of each component is internal (private) to the component. However, there are no private class methods in JavaScript. Our only equivalent is the closure; therefore, our state isn't truly private. If we can obtain a reference to the React element, we can also get its state.
React supports a feature called ref. We can create refs using the React.createRef() method, and then attach that ref to any child DOM element or React element. We can then refer to that element using the ref.
class RegistrationForm extends React.Component {
constructor(props) {
super(props);
this.email = React.createRef();
this.password = React.createRef();
}
handleRegistration = (event) => {
event.preventDefault();
event.stopPropagation();
console.log(this.email.current);
console.log(this.password.current);
}
render() {
return (
<form onSubmit={this.handleRegistration}>
<Input label="Email" type="email" ref={this.email} />
<Input label="Password" type="password" ref={this.password} />
<Button title="Register" />
</form>
)
}
}
In the preceding code, in the constructor of RegistrationForm, we created two refs, which we've assigned to this.email and this.password. We then attached these two refs to the two Input elements using the ref prop.
We can now obtain a reference to the email Input element using this.email.current. And we can obtain its state property using this.email.current.state. Try opening up the browser, type in some values in the input box and click Register; you should see each input box's current state in the console.
Next, let's update the handleRegistration method to first check the state object, to see whether the values are valid; if they are, extract and assign them to a variable.
handleRegistration = (event) => {
event.preventDefault();
event.stopPropagation();
const hasValidParams = this.email.current.state.valid && this.password.current.state.valid;
if (!hasValidParams) {
console.error('Invalid Parameters');
return;
}
const email = this.email.current.state.value;
const password = this.password.current.state.value;
}
Next, we need to hash the password, compose the request, and send it to our API server. Let's define a register function, which will provide a layer of abstract and allow our handleRegistration method to remain easy to read.
<body>
<script type="text/babel">
function register (email, digest) {
// Send the credentials to the server
const payload = { email, digest };
const request = new Request('http://localhost:8080/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
mode: 'cors',
body: JSON.stringify(payload)
})
return fetch(request)
.then(response => {
if (response.status === 200) {
return response.text();
} else {
throw new Error('Error creating new user');
}
})
}
...
</script>
</body>
These two functions use the Fetch API to send the request to our API server (assuming to be running on http://localhost:8080/).
Next, we need to call the register functions we defined earlier to actually authenticate the user.
handleRegistration = (event) => {
...
const email = this.email.current.state.value;
const password = this.password.current.state.value;
const digest = bcrypt.hashSync(password, 10));
register(email, digest))
.then(console.log)
.catch(console.error)
}
Lastly, we are using bcrypt.hashSync to hash the password; therefore, we need to load the bcryptjs library, which we can get from the RawGit CDN via the following URL: https://rawgit.com/dcodeIO/bcrypt.js/master/dist/bcrypt.min.js.
<head>
...
<script src="https://rawgit.com/dcodeIO/bcrypt.js/master/dist/bcrypt.min.js"></script>
</head>
<body>
<div id="renderTarget"></div>
<script type="text/babel">
const bcrypt = dcodeIO.bcrypt;
...
</script>
</body>