State ===== Now that we have all the bits in place, we can start adding some interactive functionality. The core principle in React is that each component has a state and the contents of that state flows down into the displayed HTML. When the user interacts with the interface, all changes are sent via events back up to the component, which then updates the state to trigger a re-rendering. This differs from Ember, where, for example, state can flow both directions, as we made use of with the ``{{input}}`` helper. Displaying State ---------------- The first step is to define the initial state of our component, by adding the following hook into it: .. code-block:: js import React, { useState } from "react"; import ReactDom from "react-dom"; import "../styles/app.scss"; function Athena() { const [email, setMail] = useState('') const [password, setPassword] = useState('') const [loginValid, setLoginValid] = useState(true) ... } In the ``initialState`` we can specify the initial state of our component. This state can then be used when rendering the component: .. code-block:: jsx render() { ... ... ... } ``useState`` is one of ten built-in hooks, that React provides. When using hooks a component can have several states. useState return an array that always has two values. The first value is the getter, the second value is the setter-function. We can use this knowledge to profit from JavaScripts **array destructuring** to create our variables that store our state, as you can see above. We can pass the initial state as parameter for ``useState``. In React ``{something}`` is the equivalent of ``{{something}}`` in handlebars and just as in handlebars, it can be used to attach variables to attributes, but also just to output text anywhere else. The difference is that in JSX the content can be any valid variable, regardless of whether it is defined on the ``state``, as in this case, or just a local variable, as we will use later in the tutorial. You can now try changing the initial state and you will see that the page reloads with the state displayed. Ignore the warning in the console, we will deal with it next. User Input ---------- The next step is to add the necessary functionality to implement user input. As stated above, state flows down, but only events flow back up. We thus need to attach event listeners to the input elements: .. code-block:: jsx render() { ... ... ... } Unlike in Ember, where we use actions to link the template to the JavaScript code, in React, we simply attach a function to the relevant event attribute. Here we want to listen to changes on the email and password fields and then call the respective event handlers. To make this work, we need to add the two event handler functions into our component: .. code-block:: js const updateEmail = (ev) => { setMail(ev.target.value) } const updatePassword = (ev) => { setPassword(ev.target.value) } This is only one way to write our functions, we could use the regular ``function (params) {}`` syntax, as well. Inside the event handlers you must always use the setter function we defined in ``setState`` to update the component's state. The function takes a single object that defines the state variables to be updated and their new values. In these two cases, we set the ``email`` and ``password`` values of the state to the value the user had typed into the input field (accessible via the ``ev.target.value``). At this point there is no visual result of updating the state. To see an effect, we will add an event listener for the form submission: .. code-block:: jsx
.. code-block:: js const handleLogin = (ev) => { ev.preventDefault(); if (email === "test@example.com" && password === "password") { setLoginValid(true); } else { setLoginValid(false); } } This will now handle the submit event, and prevent the default action of submission, but because we are not reacting to the change in the ``loginValid`` state, there is no visible result. To react to this change, we update the ``render`` method: .. code-block:: jsx let errorTag = null; if (!loginValid) { errorTag = ( No user exists with the e-mail address {email} or the password is incorrect. ); } return ( ...
... ) As you can see before the start of the ``return`` statement, we define a new variable ``errorTag``, and if the ``loginValid`` state is false, then we assigned a block of JSX to that variable. Then in the main JSX block, we can simply output the content of the ``errorTag`` variable. If it is ``null``, then nothing is displayed, whereas if it is set to the given JSX value, then that is simply inserted into the output at that point. If you try the login functionality now, you will see a nice error message in the inspector. If we wanted to just set attributes to specific values if there is an error, or if we want to actually see the error indicator, then we would use the same pattern: .. code-block:: jsx let errorTag = null; let errorLabel = null; let errorInput = null; if (!loginValid) { errorTag = ( No user exists with the e-mail address {email} or the password is incorrect. ); errorLabel = "text-danger"; errorInput = "form-control is-invalid"; } else { errorLabel = null; errorTag = null; errorInput = "form-control" } return ( ...

Login

... ) } Here we define the ``errorLabel`` and ``errorTag`` variables and given them a value if there is an error. Then in the main JSX template, we can simply attach those to the relevant attributes. React is smart enough about this, so that if the variable is ``null`` (no error), then the attribute is simply not output. Note, that we overrode the ``className`` attribute for both ``input`` elements. .. important:: Whenever you update the state, the page will be rendered again will be called. It is thus important that you do the minimal amount of processing possible in component to ensure high performance. Processing should generally be done when receiving events and the results stored in the ``state``, then they can simply be displayed.