Custom solution and membership site built on WordPress require some custom WordPress login solutions. In this tutorial we will create a WordPress Login Plugin that will use React for processing login. With the help of React, we can have more than 1 widget and each of them will change its appearance when our user logs in.
The code in this tutorial can be easily used on any of your WordPress solutions. Remember to edit the code and configure it to your needs.
Download the code react-login.
WordPress Login Form
As we always do with a plugin, we first must create a simple file with some information about our plugin.
Create a folder react-login
in the plugins
folder and also create a file inside that folder react-login.php
.
That is some simple information just so that we can see it in our Plugins menu. Go to the admin area and activate it.
Assets
In the last tutorial where we learned how to create a WordPress Widget with ReactJS and WP REST API, we have also included the React scripts. We will do the same here.
Create two files and name them react.min.js
and react-dom.min.js
. In the first one add this:
In the second one add this:
Widget
We will use a widget for our WordPress Login form so that we can easily add as many widgets as we want and test the code to see how it handles one or many login forms. Add this code to our plugin file.
We have defined our Widget ID to be react_login_widget
. We will use this ID to check if our Widget is active so that we can enqueue scripts. The method form
will create an input
element in the WordPress admin area. The next method, update
will save the title we add in the WordPress admin area. The last method widget
is the code that will be processed on the front.
In this method we are creating a simple div
element with the class="react_login"
. We are not using ID because we could have more than 1 widget here. Instead, we are using the class react_login
because the class
can be used more than once on a web page. Since we will target this class, we can place the class on many places on our web page and we will have the login form or user information. It does not have to be a widget, it could even be a simple shortcode.
I leave that up to you to decide how you will implement this into your solution 🙂
Enqueuing Scripts
Before enqueueing our scripts, we will check if our WordPress Login form widget is active. If it is, then we will enqueue the scripts for React, ReactDOM and we will also enqueue the babel script that will handle the JSX transformation of our JavaScript. This is used only for the purpose of this tutorial.
I would recommend to use babel only in development and transform your JSX using Gulp or similar before pushing it to production. In the last tutorial about React, which I have listed above, I have written React elements using vanilla JavaScript. Now, I will use the JSX so that you can see how to do it with JSX also.
Let’s enqueue them now by adding this to our WordPress Login plugin:
We are also localizing our babel-js
script before enqueueing it. You could actually use some localized strings for your JavaScript, but we will use it here to add the url to our AJAX in WordPress and to create a nonce that we can use for security reasons.
By using wpReactLogin.ajax_url
in our JavaScript, we will have the complete URL to the AJAX inside WordPress.
Coding with React in JSX
You are here mostly for this, right? 🙂 We will now code using React and JSX for our WordPress Login form. Let’s start by adding the script. We will add our script directly in the footer and that is only for the purpose of this tutorial. You should babelify your script and enqueue it as we did the other React scripts.
We will have to use the babel
indicator in our script
element, so let’s start by adding this:
Since we are using text/babel
, our babel script will read the code inside that and transform any JSX into vanilla JavaScript right inside the browser.
The React Root
Since our WordPress Login form will be able to handle more than 1 widget built with React, we have to get every single class="react_login"
element and instantiate the React Object on it using ReactDOM. Add this to our script:
We are first getting all the elements with that class. The variable reactLoginDoms
is an array and it will be used to store every ReactDOM we have instantiated. By using the for
loop we are iterating through every element we have found and we are instantiating a ReactDOM on it. Once instantiated, we are pushing it to our store reactLoginDoms
.
We are also adding a property order
to our React Root, so that we know which form has been processed. Our React Root element is ReactLogin
, so let’s add the code for it:
This looks like a lot, but bare with me. You will understand it pretty well. First, since this is our React root element that will pass the other values such as errors to other elements, we will store the state in it. We are setting an initial state in the method setInitialState
.
Checking the Fields
The second method checkFields
is our custom method for checking fields. This method will be called before processing the form. First, we are getting the order number so that we know which form is processed. The variables $username
and $password
are self-explanatory and they are defined as empty. We have done that because we do not know yet how to get the values.
The next step is to find out if there are more than 1 form. If there are, we are getting the value by using the order
value. If there is only 1 form, we are then getting the value directly. The name reactLoginForm
will be placed as the attribute name
on our forms and that is why we can get the values using that code.
The same will be done with the username and the password inputs.
After that, we are setting an empty array of errors. If any input is empty, we are adding an error saying that the input is empty. After that, we are setting the state with the errors.
Handling the Form
The method handleForm
will be called when the form is submitted. We will prevent the default processing and use our own. This is done by using the e.preventDefault()
. After that, we are checking the fields for errors. If there aren’t any errors, we are processing an AJAX request.
We are setting the data
that is passed in our AJAX request. The action
part will be important when process the AJAX on the backend because we will use it for hooking in our function. To learn more about using AJAX in WordPress read AJAX in Plugins.
To get the values for our username
and password
, we are using the same approach as when we checked for errors. After all data is set, we are creating an AJAX request with the POST method.
If the received data has a parameter success
and it is false
, then it means we got an error. We are then getting that error message
and save it to our state. This will trigger the ReactDOM to re-render and show our error.
If the success
is true
, then we will update all our ReactDOMs with that information. With the for
loop we are iterating through each of our ReactDOMs that is stored in the reactLoginDoms
. While iterating, we are setting the state to each of those ReactDOMs. In that state, we are setting the logged
to be true and we are also setting the user data that we have received from WP_User object. The user data in that object is stored inside the data
variable.
For any error that was given by the network or bugs within the code, we are using the alert
but you can create your own error handling solution.
When the Component Mounted
When the React component did mount and it is ready to use, there is a method for initial settings. That method is componentDidMount
In this method, we are creating another AJAX request to our WordPress site and we are receiving the data if the user is already logged in or not. If it is we are again using the same approach as we did when handling the form. We will set the state with the same parameters.
Rendering the Root element
The last method is render
and this method is deciding if we are going to see a login form or some user information. First, we are setting a $renderElement
using the JSX for handling React Form element. On that element, we are passing some properties:
- error – the array of errors
- handleForm – the method for handling forms
We are creating a reverse flow by passing our method for handling forms. This is called reverse because we will add that method on our form attribute onSubmit
. Our form will be rendered by another React Object ReactLoginForm
. Once our form is submitted, we will call the method from our parent React root element ReactLogin
.
If the user is logged in, then we will not render the ReactLoginForm
, but instead we will render the ReactUserData
. We are passing the user object there, so that we can use any data given by WP_User.
React Login Form
Our React root element is done. We now know that when our user is not logged in, he will see a login form. We will create that by rendering the React ReactLoginForm
element. Let’s add that to our code:
We only have the render
method defined here. We are creating the form with the attributes:
- name: reactLoginForm – remember that we use that when retrieving values
- onSubmit: {this.props.handleForm} – using {} to point out that this is not a string, but an object value. This value will be the method from our React root element
Before rendering the form, we are checking for errors. If there are any errors passed to our ReactLoginForm
, we will render them using JSX in a ul
element. We are also using dangerouslySetInnerHTML
so that React won’t render the HTML as a string, but as good raw HTML. This will ensure that any links or other HTML elements provided in error messages gets properly rendered.
Don’t use that if you don’t trust the source of the HTML
We are then rendering the $errors
above all the form fields.
React User Data
In the ReactUserData
we will show only some simple data for the user. We will say hello to it and display his/her display name.
You can do a lot more to it because you have all the user data that is passed from our AJAX response. The data that is passed from WP_User can be found here.
Handling AJAX Requests
So far we have done very much. We have coded all our React objects and we only require the response form our AJAX requests. For you to better understand, I have divided them into two requests:
- react_check_if_logged – Used on
componentDidMount
- react_login_user – Used on
handleForm
Checking if the User is Logged in
The first action react_check_if_logged
is passed in our AJAX request when our component mounts. This is only checking if our user is already logged in.
We are here using both AJAX action hooks wp_ajax_
and wp_ajax_nopriv
. The nopriv0
then we are simply sending a JSON reponse with the success=0
.
If the ID is something other than 0
, we are sending a JSON response with the success being 1
and also the WP_User object.
Logging in the user
The second action react_login_user
is passed in out AJAX request when the WordPress login form is submitted.
Since, we are posting data and interacting with our site, for security reasons, we are checking the nonce. If the nonce is correct, we are getting the username and password from the posted data. After we receive the data, we are calling wp_authenticate.
If the authentication did not pass because the username or the password is wrong, we will get a WP_Error object. If that is the case, we are then sending a JSON response with the success being 0
. We are also passing a message
retrieved from WP_Error.
If the authentication passed, we are sending a JSON response with the success being 1
and the user object retrieved from WP_User. Before sending that JSON, we are also setting the authentication cookie so that the logged in information is still there even after refreshing the page.
Conclusion
In this tutorial we have created a good WordPress Login form. We created a widget that can be placed around our WordPress website and share the same information if the user is logged in. Using React we have created a seemingly complex widget using some simple JavaScript (JSX).
Since our ReactDOM only targets the class react_login
, we don’t even need a Widget for it.
If you have any question or suggestion, please do post them in the comments below.
Become a Sponsor
Hello Igor,
I see that you have used vanilla html controls in your react component and then integrated reactjs with WordPress. However I am still unable to wrap my head around integrating prebuilt react UI components like
https://www.npmjs.com/package/react-player
https://video-react.js.org/
https://fritz-c.github.io/react-image-lightbox/
in my react component and then integrating the component with WordPress. How would we do the same?
I have been told to build the component (with the prebuilt react UI components), generate the bundled javascript\css files for the component using webpack (or the create-React-app build scripts) and then enqueue them in wordpress. This however does not work. please see here: https://stackoverflow.com/questions/49142418/how-to-fix-exports-is-not-defined-error-on-create-react-app-project
Similarly in this tutorial, you have a added a “No recommended in production, better to babelify your script”, when registering\enqueuing babel-js scripts. Can you provide more details around the same, in your next tutorial?
I have an article on setting up Webpack 3 to work with ES6 and later so that might help you. https://www.ibenic.com/configuring-webpack-wordpress/
I have not yet encountered such issue so I could not give you any advice from my own experience. But if you do happen to solve it, please let me know in a reply on this comment.
I installed this and I’m getting an error “Target container is not a DOM element”. How do I fix this?
Hi Ilene, I’ve tried it and it works fine on my side. Can you check if all the code is written correctly?
Hi Igor,
Thanks for this great tutorial. Is it really necessary to load the two react scripts ?
Can we just use [‘wp-element’] as a dependency when we register our script ?
And then using wp.element instead of React in the js ?
Thanks a lot
Hi Ravon, this tutorial was done before Gutenberg started so such dependencies were not yet available. You could try doing that, just be sure the wp.element dependency has the React DOM element that you can use to load the app onto an HTML element.
Wow so fast an answer : thx a lot !