React UI Tutorial

REACT Basic

React UI Tutorial: Buttons, Edit Boxes, Comboboxes, And Backend API Data

This tutorial focuses on the user interface part of React.

We shall learn:

– What a React component is

– How to create a button

– How to create an edit box

– How to create a combobox

– How to store UI values with React state

– How to send data to a backend API

– How to read data from a backend API

The examples use your Tank Monitor backend:

text

http://127.0.0.1:5000

1. What React Does

React helps you build a user interface with small pieces called components.

Example component:

jsx

function App() {
  return (
Tank Monitor
  );
}

A component is just a JavaScript function that returns UI.

2. Important React Idea: State

State means data that can change while the page is open.

Examples:

– Text typed into an edit box

– Selected value in a combobox

– Data loaded from PostgreSQL through the backend API

– Loading or error messages

React uses ‘useState’  for this:

jsx

import { useState } from "react";
function App() {
  const [tankName, setTankName] = useState("");
  return (
    <input
      value={tankName}
      onChange={(event) => setTankName(event.target.value)}
    />
  );
}

Meaning:

text

tankName

is the current value.

text

setTankName

changes the value.

3. Button Example

A React button looks like normal HTML, but you attach a JavaScript function with ‘onClick’ .

jsx

function App() {
  function handleButtonClick() {
    console.log("Button was pressed");
  }
  return (
<button onClick={handleButtonClick}> Save </button>     
  );
} 

 

When the user presses the button, React runs:

javascript

handleButtonClick()

4. Edit Box Example

An edit box is usually an ‘<input>’

jsx

import { useState } from "react";
function App() {
  const [operatorName, setOperatorName] = useState("");
  return (
            <input
        type="text"
        value={operatorName}
        onChange={(event) => setOperatorName(event.target.value)}
      />
Current value: {operatorName}
  );
}

Important parts:

jsx

value={operatorName}

The input displays the React state value.

jsx

onChange={(event) => setOperatorName(event.target.value)}

When the user types, React updates the state.

This is called a controlled input.

5. Number Edit Box Example

For numbers:

jsx

import { useState } from "react";
function App() {
  const [tankLevel, setTankLevel] = useState(0);
  return (
            <input
        type="number"
        value={tankLevel}
        onChange={(event) => setTankLevel(Number(event.target.value))}
      />
Tank level: {tankLevel}
  );
}

‘event.target.value’  is normally text, so ‘Number(…)’  converts it to a number.

6. Date And Time Edit Box

For tank full and empty time:

JSX

import { useState } from "react";
function App() {
  const [tankFullTime, setTankFullTime] = useState("");
  return (
            <input
        type="datetime-local"
        value={tankFullTime}
        onChange={(event) => setTankFullTime(event.target.value)}
      />
{tankFullTime}
  );
}

The browser shows a date/time picker.

7. Combobox Example

In HTML, a combobox is a ‘

jsx

import { useState } from “react”;

 

function App() {
  const [tankStatus, setTankStatus] = useState("full");
  return (
    <div>
      <label>Tank status</label>
      <select
        value={tankStatus}
        onChange={(event) => setTankStatus(event.target.value)}
      >
        <option value="full">Full</option>
        <option value="empty">Empty</option>
        <option value="running">Running</option>
      </select>
      <p>Selected status: {tankStatus}</p>
    </div>
  );
}

The state value is:

javascript

tankStatus

The function that changes it is:

javascript

setTankStatus

8. Loading Data From Backend API

To read tank data from your backend:

text

GET http://127.0.0.1:5000/api/tank-times

React code:

jsx

import { useEffect, useState } from "react";
function App() {
  const [tankRows, setTankRows] = useState([]);
  const [error, setError] = useState("");
  async function loadTankRows() {
    try {
      const response = await fetch("http://127.0.0.1:5000/api/tank-times");
      const data = await response.json();
      if (!response.ok) {
        throw new Error(data.error || "Could not load tank rows");
      }
      setTankRows(data);
    } catch (error) {
      setError(error.message);
    }
  }
  useEffect(() => {
    loadTankRows();
  }, []);
  return (
    <main>
      <h1>Tank Rows</h1>
      {error && <p>{error}</p>}
      <button onClick={loadTankRows}>
        Refresh
      </button>
      <pre>{JSON.stringify(tankRows, null, 2)}</pre>
    </main>
  );
}

Important:

jsx

useEffect(() => {
  loadTankRows();
}, []);

This runs once when the page opens.

9. Showing API Data In A Table

Instead of showing JSON, show a table:

jsx

function TankTable({ rows }) {
  return (
    <table>
      <thead>
        <tr>
          <th>ID</th>
          <th>Tank Full Time</th>
          <th>Tank Empty Time</th>
          <th>Duration Minutes</th>
        </tr>
      </thead>
      <tbody>
        {rows.map((row) => (
          <tr key={row.id}>
            <td>{row.id}</td>
            <td>{row.tankFullTime}</td>
            <td>{row.tankEmptyTime || "Open"}</td>
            <td>{row.durationMinutes ?? "Open"}</td>
          </tr>
        ))}
      </tbody>
    </table>
  );
}

Use it like this:

JSX

<TankTable rows={tankRows} />

 

10. Sending Data To Backend API

To save a completed tank row, call:

text

POST http://127.0.0.1:5000/api/tank-times

React example:

jsx

import { useState } from "react";
function App() {
  const [tankFullTime, setTankFullTime] = useState("");
  const [tankEmptyTime, setTankEmptyTime] = useState("");
  const [message, setMessage] = useState("");
  async function saveTankTime() {
    try {
      const response = await fetch("http://127.0.0.1:5000/api/tank-times", {
        method: "POST",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify({
          tankFullTime: tankFullTime,
          tankEmptyTime: tankEmptyTime
        })
      });
      const result = await response.json();
      if (!response.ok) {
        throw new Error(result.error || "Could not save tank time");
      }
      setMessage("Saved successfully");
    } catch (error) {
      setMessage(error.message);
    }
  }
  return (
    <main>
      <label>Tank full time</label>
      <input
        type="datetime-local"
        value={tankFullTime}
        onChange={(event) => setTankFullTime(event.target.value)}
      />
      <label>Tank empty time</label>
      <input
        type="datetime-local"
        value={tankEmptyTime}
        onChange={(event) => setTankEmptyTime(event.target.value)}
      />
      <button onClick={saveTankTime}>
        Save
      </button>
      <p>{message}</p>
    </main>
  );
}

 

11. Form Submit Example

For real forms, prefer ‘onSubmit’.

jsx

function App() {
  const [tankFullTime, setTankFullTime] = useState("");
  const [tankEmptyTime, setTankEmptyTime] = useState("");
  async function handleSubmit(event) {
    event.preventDefault();
    await fetch("http://127.0.0.1:5000/api/tank-times", {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify({
        tankFullTime,
        tankEmptyTime
      })
    });
  }
  return (
    <form onSubmit={handleSubmit}>
      <input
        type="datetime-local"
        value={tankFullTime}
        onChange={(event) => setTankFullTime(event.target.value)}
      />
      <input
        type="datetime-local"
        value={tankEmptyTime}
        onChange={(event) => setTankEmptyTime(event.target.value)}
      />
      <button type="submit">
        Save
      </button>
    </form>
  );
}

This line is important:

javascript

event.preventDefault();

It stops the browser from refreshing the page when the form is submitted.

12. Full UI Example

This example includes:

– Edit boxes

– Combobox

– Button

– Backend GET

– Backend POST

– Table

 jsx


import { useEffect, useState } from "react";
function App() {
  const [tankFullTime, setTankFullTime] = useState("");
  const [tankEmptyTime, setTankEmptyTime] = useState("");
  const [tankStatus, setTankStatus] = useState("completed");
  const [rows, setRows] = useState([]);
  const [message, setMessage] = useState("");
  async function loadRows() {
    try {
      const response = await fetch("http://127.0.0.1:5000/api/tank-times");
      const data = await response.json();
      if (!response.ok) {
        throw new Error(data.error || "Could not load rows");
      }
      setRows(data);
    } catch (error) {
      setMessage(error.message);
    }
  }
  async function saveRow(event) {
    event.preventDefault();
    try {
      const response = await fetch("http://127.0.0.1:5000/api/tank-times", {
        method: "POST",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify({
          tankFullTime,
          tankEmptyTime,
          tankStatus
        })
      });
      const result = await response.json();
      if (!response.ok) {
        throw new Error(result.error || "Could not save row");
      }
      setMessage("Saved successfully");
      setTankFullTime("");
      setTankEmptyTime("");
      await loadRows();
    } catch (error) {
      setMessage(error.message);
    }
  }
  useEffect(() => {
    loadRows();
  }, []);
  return (
    <main>
      <h1>Tank Monitor</h1>
      <form onSubmit={saveRow}>
        <label>Tank full time</label>
        <input
          type="datetime-local"
          value={tankFullTime}
          onChange={(event) => setTankFullTime(event.target.value)}
        />
        <label>Tank empty time</label>
        <input
          type="datetime-local"
          value={tankEmptyTime}
          onChange={(event) => setTankEmptyTime(event.target.value)}
        />
        <label>Status</label>
        <select
          value={tankStatus}
          onChange={(event) => setTankStatus(event.target.value)}
        >
          <option value="completed">Completed</option>
          <option value="open">Open</option>
          <option value="manual">Manual</option>
        </select>
        <button type="submit">Save</button>
      </form>
      <button onClick={loadRows}>Refresh</button>
      <p>{message}</p>
      <table>
        <thead>
          <tr>
            <th>ID</th>
            <th>Full</th>
            <th>Empty</th>
            <th>Duration</th>
          </tr>
        </thead>
        <tbody>
          {rows.map((row) => (
            <tr key={row.id}>
              <td>{row.id}</td>
              <td>{row.tankFullTime}</td>
              <td>{row.tankEmptyTime || "Open"}</td>
              <td>{row.durationMinutes ?? "Open"}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </main>
  );
}
export default App;

13. How UI Data Links To Backend API

The UI does not talk directly to PostgreSQL.

The UI talks to Node.js:

javascript

fetch("http://127.0.0.1:5000/api/tank-times")

Node.js talks to PostgreSQL:

javascript

pool.query("SELECT ... FROM tank_times")

The flow is:

text

React UI

  input values

    |

    v

fetch()

    |

    v

Node.js API

    |

    v

PostgreSQL

14. Common UI Pattern

Most React UI screens follow this pattern:

text

1. Create state variables

2. Show input controls

3. Update state when the user types or selects

4. Press a button

5. Send state values to backend API with fetch()

6. Show result or error message

7. Reload table data

Example:

JSX

const [name, setName] = useState("");
<input
  value={name}
  onChange={(event) => setName(event.target.value)}
/>
<button onClick={saveName}>Save</button>

15. Quick Summary

Button:

jsx

Edit box:

JSX

<input value={name} onChange={(event) => setName(event.target.value)} />

Combobox:

JSX

<select value={status} onChange={(event) => setStatus(event.target.value)}>
  <option value="full">Full</option>
  <option value="empty">Empty</option>
</select>

GET from backend:

javascript

const response = await fetch("http://127.0.0.1:5000/api/tank-times");
const data = await response.json();

POST to backend:

javascript

await fetch("http://127.0.0.1:5000/api/tank-times", {
  method: "POST",
  headers: {
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    tankFullTime,
    tankEmptyTime
  })
});

See Also :