React + Firestore Fake Weather

Dette eksempel bygger videre på react eksemplet med firestore, og demonstrationen af hvordan man kan uploade data med node.js.

Statisk data visning

Først laves en fil med samme struktur som forventes at være i firestore databasen, når upload scriptet har kørt nogle gange. Data strukturen laves i filen src/lib/fake_weather.js, med dette indhold.

import firebase from "../lib/Firebase";

const Timestamp =  firebase.firestore.Timestamp;

// helper for generating firestore timestamps
const t = timeString => Timestamp.fromDate(new Date(timeString));

// Fake data - could come from a database or similar
const data = [
  {
    lastUpdate: t('2020-03-13T11:30:42'),
    key: "dataIdOne",
    windMeasurements: [
      { time: t('2020-03-13T10:20:42'), windSpeed: 12, windDir: 277 },
      { time: t('2020-03-13T11:30:42'), windSpeed: 7, windDir: 270 },
    ]
  },
  {
    lastUpdate: t("2020-03-14T09:31:10"),
    key: "dataIdTwo",
    windMeasurements: [
      { time: t("2020-03-14T09:07:34"), windSpeed: 17, windDir: 179 },
      { time: t("2020-03-14T09:31:10"), windSpeed: 25, windDir: 183 },
      { time: t("2020-03-14T11:30:42"), windSpeed: 12, windDir: 180 },
      { time: t("2020-03-14T12:37:20"), windSpeed: 23, windDir: 171 },
    ]
  },
];

export default data;

Derefter laves en ny react komponent i src/components/Weather.js, som viser indholdet i browseren.

import React, { useEffect, useState } from "react";

import fakeWeatherData from '../lib/fake_weather';

function Weather() {
  return (
    <div>
      <h1>Fake Weather data (from file)</h1>
      <MeasurementList weatherData={fakeWeatherData} />
    </div>
  );
}

const MeasurementList = props => {
  return (
    <div>
      {
        props.weatherData.map(it => (
          <MeasurementDay key={it.key} item={it} />
        ))
      }
    </div>
  )
}

const MeasurementDay = props => {
  return (
    <div>
      <h2>Fake wind data, Updated: {props.item.lastUpdate.toDate().toLocaleString()}</h2>
      <table>
        <thead>
          <tr>
            <th>Time</th>
            <th>Wind speed</th>
            <th>Wind direction</th>
          </tr>
        </thead>

        <tbody>
          {
            props.item.windMeasurements.map((it, i) => (
              <tr key={i}>
                <td>{it.time.toDate().toISOString()}</td>
                <td>{it.windSpeed}</td>
                <td>{it.windDir}</td>
              </tr>
            ))
          }
        </tbody>
      </table>
    </div>
  );
};

export default Weather;

Bemærk hvordan vi først importeret datastrukturen fra filen src/lib/fake_weather.js, sender den ind i MeasurementList komponenten og itererer over indholdet af data i MeasurementList og MeasurementBody komponenterner.

Weather komponenten indsættes i src/App.js, således:

import React from 'react';
import './App.css';

import Clock from './components/Clock'
import Highscore from './components/Highscore';
import Hotdog from './components/Hotdog';
import Weather from './components/Weather';

function App() {
  return (
    <div className="App">
      <Clock />
      <Hotdog />
      <Weather />
      <Highscore />
    </div>
  );
}

export default App;

Firestore itegration

For at hente data ud fra firestore skal der tilføjes mere kode i Weather komponenten.

import React, { useEffect, useState } from "react";

import fakeWeatherData from '../lib/fake_weather';

// import the firebase configuration settings
import firebase from "../lib/Firebase";

// initialize firestore
const firestore = firebase.firestore();

function Weather() {

  const handleData = snapshot => {
    if (snapshot.empty) {
      console.log("No matching documents");
      return;
    }

    let weatherDataArray = [];

    snapshot.forEach(doc => {
      let myData = doc.data();
      myData["key"] = doc.id;
      weatherDataArray.push(myData);
    })

    setWeatherData(weatherDataArray);
  };

  const subscribeToRealtimeUpdates = () => {
    const query = firestore.collection("weather")
      .orderBy('lastUpdate', 'desc')
      .limit(3);
    query.onSnapshot(handleData);
  };

  useEffect(
    () => {
      subscribeToRealtimeUpdates();
    },
    // provide empty array to avoid infinite loop
    []
  );

  const [weatherData, setWeatherData] = useState([]);

  return (
    <div>
      <h1>Fake Weather data from firebase</h1>
      <MeasurementList weatherData={weatherData} />

      <h1>Fake Weather data (from file)</h1>
      <MeasurementList weatherData={fakeWeatherData} />
    </div>
  );
}

const MeasurementList = props => {
  return (
    <div>
      {
        props.weatherData.map(it => (
          <MeasurementDay key={it.key} item={it} />
        ))
      }
    </div>
  )
}

const MeasurementDay = props => {
  return (
    <div>
      <h2>Fake wind data, Updated: {props.item.lastUpdate.toDate().toLocaleString()}</h2>
      <table>
        <thead>
          <tr>
            <th>Time</th>
            <th>Wind speed</th>
            <th>Wind direction</th>
          </tr>
        </thead>

        <tbody>
          {
            props.item.windMeasurements.map((it, i) => (
              <tr key={i}>
                <td>{it.time.toDate().toISOString()}</td>
                <td>{it.windSpeed}</td>
                <td>{it.windDir}</td>
              </tr>
            ))
          }
        </tbody>
      </table>
    </div>
  );
};

export default Weather;

Først importeres firebase konfigurationen, og firebase initialiseres.

Derefter bruges oprettes forbindelse til firebase og der abbonneres på databaseopdateringer.

Endelig tilføjes en enstra MeasurementList komponent, så indholdet vises på skærmen.

Hvis appen startes, vi der nu automatisk opdateres med nye målinger i tabellerne, når de tilføjes til firestore databasen med upload scriptet.

App Demo

Denne demo viser resultatet af anstrengelserne og skulle gerne minde om det du kan se på figuren herunder.

Når du åbner siden burde du se noget i stil med det der er vist på figuren herunder.

Bemærk: For at kunne tilføje data til databasen, kræver det at du selv laver det på din egen maskine.

Screenshot af den kørende react app.

Materiale