React e NextJS

Introduzione a React

Inserire React in una pagina
Partiamo da un semplice index.html (potrebbe essere lo starter template di Bootstrap).

Prepariamo un div "root", il collegamento a index.js e carichiamo le librerie necessarie come spiegato qui.

<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

Creiamo index.js

e index.css

 

 


Creare un componente
È possibile memorizzare un elemento di React in una variabile:

Questo elemento però è fisso, è poco più di un semplice blocco di HTML.

La potenza di React si scatena con i componenti, che a differenza degli elementi possono essere configurati a piacere.

Immaginiamo un componente card al quale possiamo passare la foto, il titolo, ecc. come a una funzione. E infatti uno dei modo per creare i componenti è quello mediante funzioni:

Questo esempio inizialmente non funzionerà: utilizza la console del browser per risolvere i vari errori.
Il problema più frequente sarà la presenza di elementi non autoconclusi: ad esempio dovrai usare <input /> e non <input>.

Nota l'iniziale maiuscola nel nome della funzione.

Nota come è stato richiamato nel metodo render.

Nota: avremmo potuto richiamarlo con Navbar() come una normale funzione, ma così è molto più semplice inserirlo nell'HTML.

 


Stilizzare i componenti


Per applicare gli stili ai componenti si usa l'attributo className, al posto di class:

Si procede poi creando gli stili come di consueto, nel file .css

 

Inoltre per usare l'attributo style devi fare in questo modo:

nota: di fatto stai passando un oggetto JavaScript con una proprietà width.

 


Componenti nidificati


Volendo passare a render più componenti bisogna inserirli in un div che li contenga: questo "nodo" andrà ad innestarsi nell'elemento #root del DOM (Modello a Oggetti del Documento)

 


Tag a chiusura automatica


Normalmente i tag HTML sono in coppie di apertura e chiusura, del tipo:

ma ci sono alcuni tag, come ad es. <input>, che non hanno bisogno di un tag di chiusura.

Per utilizzarli in JSX senza riceve un errore bisogna utilizzare il simbolo / che indica che il tag si chiude in sè stesso, come nell'esempio:

 


Componenti in file separati


Per tenere in ordine il codice, è buona norma creare un file separato per ogni componente o gruppo di componenti affini, importandoli poi con dei tag script:

 

Ricorda che i file vengono letti in ordine, quindi se index.js usa un componente presente in Navbar.js, mettere gli script in questo modo non funzionerebbe, perché quando index.js viene eseguito non sa ancora che cosa sia il componente Navbar:


Passare le proprietà a un componente
In questo esempio vediamo una pagina che usa la griglia di Bootstrap per visualizzare tre Card, passando a ognuna un diverso attributo titolo:

Il componente che disegna la card è questo. Gli attributi vengono passati nell'oggetto props, che è possibile utilizzare per mostrare una specifica proprietà, come ad esempio props.titolo:

Nota: quando in JSX vogliamo scrivere un blocco di JavaScript puro, lo mettiamo tra parentesi graffe, come ad esempio in { props.titolo }.

 

Ecco come comporre il percorso di un file per passarlo come prop a un elemento img:

 

Destrutturare le props


È possibile prendere l'oggetto props ricevuto e destrutturarlo in singole variabili:

 


Passare props diverse da stringhe


In questo esempio vediamo come passare dei valori booleani, numerici o addirittura degli array come props, inserendoli tra parentesi graffe (ricorda che il codice all'interno viene valutato come semplice JavaScript):

Introduzione a NextJS

Creare l'app di esempio
npx create-next-app@latest

Scelte:

Se vi compare un errore relativo all'aggiornamento di npm o altro fate così:
npm install -g npm@latest

Se compare un errore relativo all'esecuzione di script in PowerShell aprirla come amministratore ed eseguire:
Set-ExecutionPolicy Unrestricted

#todo

Apri la cartella dell'applicazione su Visual Studio Code

Dal terminale:
npm run dev


#todo

Apri nel browser l'url dell'applicazione:

Collegarsi via Browser a localhost:3000

Modificare la pagina iniziale

#todo

Apri pages/index.js e modificalo in modo che appaia così:
Da quale file vengono importati gli stili CSS?
Come vengono usati poi all'interno della pagina?

Creare altre pagine
Creiamo una nuova pagina inserendo un nuovo file .js nella cartella pages:


Le rotte

pages/index.js sarà associato alla rotta /
pages/posts/primo-post.js sarà associato alla rotta /posts/primo-post

#todo

Usa il browser per visitare la nuova pagina.


Il componente Link

Apriamo index.js e importiamo il componente:

Lo usiamo poi per modificare il link che compare nel titolo:
Il componente Link non è un semplice link: si occupa anche di precaricare le pagine che il visitatore potrebbe selezionare, in modo da rendere l'esperienza di navigazione più reattiva e veloce.

Il componente Head

Nota che in index.js viene fatto uso del componente Head per impostare il titolo della pagina e importare la favicon.


#todo

Usa quello che hai imparato per creare anche nella pagina primo-post un link che rimanda alla home, inserendolo dentro un elemento h2.
Inserisci anche il componente Head per personalizzare il title della pagina.

Creare il componente Layout
Creiamo un componente Layout che sarà condiviso da tutte le pagine. Ci servirà per definire gli stili del sito.
Lo metteremo nella cartella components:

#todo

Vai nella pagina primo-post e importa il componente appena creato.

Usiamo poi il componente per racchiudere tutto il contenuto della pagina:

Definire stili CSS
Andremo ora a collegare degli stili CSS al componente Layout:


Per usare questa classe container appena definita dentro al componente layout dobbiamo importare il file CSS e assegnargli un nome (styles).
Potremo quindi usarlo nell'attributo className del div:
Il modulo CSS creerà per noi una classe layout_container__2t4v2 ... potremo quindi creare delle classi con nomi semplici nei diversi moduli senza preoccuparci che possano interferire le une con le altre.
Inoltre Next.js si occuperà di caricare solo i moduli necessari, rendendo il caricamento della pagina più veloce.


Stili globali

Per caricare degli stili CSS in tutte le pagine dell'applicazione, crea questo file:
App è un componente React di alto livello che racchiude tutte le pagine dell'applicazione: in questo modo sarà possibile mantenere lo stato navigando tra le varie pagine, o aggiungere degli stili globali come stiamo facendo.



Rifinitura del layout
Crea una cartella images dentro a public.
Salva al suo interno una foto profilo di circa 500 x 500 pixel.

Il componente Image di Next.js ottimizza il caricamento delle immagini: agli schermi piccoli saranno inviate versioni ridotte dell'immagine, e le immagini saranno caricate solo quando l'utente scrollerà sulla parte della pagina che le contiene (lazy loading).

Aggiungi queste classi al modulo CSS del componente Layout:

.header {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.backToHome {
  margin: 3rem 0 0;
}

Aggiungi un nuovo file styles/utils.module.css che conterrà alcune classi utili da utilizzare nei vari componenti:

.heading2Xl {
  font-size: 2.5rem;
  line-height: 1.2;
  font-weight: 800;
  letter-spacing: -0.05rem;
  margin: 1rem 0;
}

.headingXl {
  font-size: 2rem;
  line-height: 1.3;
  font-weight: 800;
  letter-spacing: -0.05rem;
  margin: 1rem 0;
}

.headingLg {
  font-size: 1.5rem;
  line-height: 1.4;
  margin: 1rem 0;
}

.headingMd {
  font-size: 1.2rem;
  line-height: 1.5;
}

.borderCircle {
  border-radius: 9999px;
}

.colorInherit {
  color: inherit;
}

.padding1px {
  padding-top: 1px;
}

.list {
  list-style: none;
  padding: 0;
  margin: 0;
}

.listItem {
  margin: 0 0 1.25rem;
}

.lightText {
  color: #666;
}

Sostituisci il codice del componente Layout con il seguente, mettendo il tuo nome  nella variabile name:

import Head from 'next/head';
import Image from 'next/image';
import styles from './layout.module.css';
import utilStyles from '../styles/utils.module.css';
import Link from 'next/link';

const name = 'Your Name';
export const siteTitle = 'Next.js Sample Website';

export default function Layout({ children, home }) {
  return (
    <div className={styles.container}>
      <Head>
        <link rel="icon" href="/favicon.ico" />
        <meta
          name="description"
          content="Learn how to build a personal website using Next.js"
        />
        <meta
          property="og:image"
          content={`https://og-image.vercel.app/${encodeURI(
            siteTitle,
          )}.png?theme=light&md=0&fontSize=75px&images=https%3A%2F%2Fassets.vercel.com%2Fimage%2Fupload%2Ffront%2Fassets%2Fdesign%2Fnextjs-black-logo.svg`}
        />
        <meta name="og:title" content={siteTitle} />
        <meta name="twitter:card" content="summary_large_image" />
      </Head>
      <header className={styles.header}>
        {home ? (
          <>
            <Image
              priority
              src="/images/profile.jpg"
              className={utilStyles.borderCircle}
              height={144}
              width={144}
              alt=""
            />
            <h1 className={utilStyles.heading2Xl}>{name}</h1>
          </>
        ) : (
          <>
            <Link href="/">
              <Image
                priority
                src="/images/profile.jpg"
                className={utilStyles.borderCircle}
                height={108}
                width={108}
                alt=""
              />
            </Link>
            <h2 className={utilStyles.headingLg}>
              <Link href="/" className={utilStyles.colorInherit}>
                {name}
              </Link>
            </h2>
          </>
        )}
      </header>
      <main>{children}</main>
      {!home && (
        <div className={styles.backToHome}>
          <Link href="/">← Back to home</Link>
        </div>
      )}
    </div>
  );
}

Infine aggiorna anche il contenuto di index.js:
import Head from 'next/head';
import Layout, { siteTitle } from '../components/layout';
import utilStyles from '../styles/utils.module.css';

export default function Home() {
  return (
    <Layout home>
      <Head>
        <title>{siteTitle}</title>
      </Head>
      <section className={utilStyles.headingMd}>
        <p>[Your Self Introduction]</p>
        <p>
          (This is a sample website - you’ll be building a site like this on{' '}
          <a href="https://nextjs.org/learn">our Next.js tutorial</a>.)
        </p>
      </section>
    </Layout>
  );
}

#todo
Aggiungi un Link alla pagina primo-post e modifica il testo della home page a tuo piacimento.

Pre-rendering

Gli articoli del blog
Creiamo una nuova cartella posts (diversa da pages/posts) che conterrà un file per ogni post del nostro blog.

Creiamo il file posts/pre-rendering.md:
---
title: 'Two Forms of Pre-rendering'
date: '2020-01-01'
---

Next.js has two forms of pre-rendering: **Static Generation** and **Server-side Rendering**. The difference is in **when** it generates the HTML for a page.

- **Static Generation** is the pre-rendering method that generates the HTML at **build time**. The pre-rendered HTML is then _reused_ on each request.
- **Server-side Rendering** is the pre-rendering method that generates the HTML on **each request**.

Importantly, Next.js lets you **choose** which pre-rendering form to use for each page. You can create a "hybrid" Next.js app by using Static Generation for most pages and using Server-side Rendering for others.

Creiamo il file posts/ssg-ssr.md:
---
title: 'When to Use Static Generation v.s. Server-side Rendering'
date: '2020-01-02'
---

We recommend using **Static Generation** (with and without data) whenever possible because your page can be built once and served by CDN, which makes it much faster than having a server render the page on every request.

You can use Static Generation for many types of pages, including:

- Marketing pages
- Blog posts
- E-commerce product listings
- Help and documentation

You should ask yourself: "Can I pre-render this page **ahead** of a user's request?" If the answer is yes, then you should choose Static Generation.

On the other hand, Static Generation is **not** a good idea if you cannot pre-render a page ahead of a user's request. Maybe your page shows frequently updated data, and the page content changes on every request.

In that case, you can use **Server-Side Rendering**. It will be slower, but the pre-rendered page will always be up-to-date. Or you can skip pre-rendering and use client-side JavaScript to populate data.


Estrazione dei metadati
Ogni post ha una sezione iniziale di metadati (il titolo e la data del post). Per poterli estrarre installiamo la libreria graymatter:

npm install gray-matter

Crea una cartella lib e al suo interno un file posts.js:

import fs from 'fs';
import path from 'path';
import matter from 'gray-matter';

const postsDirectory = path.join(process.cwd(), 'posts');

export function getSortedPostsData() { // Get file names under /posts const fileNames = fs.readdirSync(postsDirectory); const allPostsData = fileNames.map((fileName) => { // Remove ".md" from file name to get id const id = fileName.replace(/\.md$/, ''); // Read markdown file as string const fullPath = path.join(postsDirectory, fileName); const fileContents = fs.readFileSync(fullPath, 'utf8'); // Use gray-matter to parse the post metadata section const matterResult = matter(fileContents); // Combine the data with the id return { id, ...matterResult.data, }; }); // Sort posts by date return allPostsData.sort((a, b) => { if (a.date < b.date) { return 1; } else { return -1; } }); }

Per il momento non preoccuparti di capire come funziona questo codice: si tratta di una funzione che prende i file dal file system (fs) e legge i metadati di ogni file usando gray-matter.

Creazione dell'indice dei blog
Useremo il metodo getStaticProps() di NextJS per riempire la nostra pagina index coi metadati dei singoli post.

Static generation with data

Nel server di produzione questa funzione viene eseguita una sola volta quando si costruisce il sito (build): NextJS prepara le pagine di HTML vero e proprio che i browser riceveranno. Il caricamento delle pagine così sarà più veloce.

#toDo

Apri la pagina index e importa la funzione creata al passaggio precedente:

import { getSortedPostsData } from '../lib/posts';
Esporta la funzione getStaticProps che informa NextJS del fatto che vuoi procedere a una generazione statica della pagina, dopo aver importato i dati dei post:

export async function getStaticProps() {
  const allPostsData = getSortedPostsData();
  return {
    props: {
      allPostsData,
    },
  };
}
La funzione restituisce un oggetto contenente i metadati di tutti i post (allPostsData), che saranno passati quindi sotto forma di props al componente Home della pagina con questa piccola modifica:

Per visualizzare l'elenco dei post creiamo una nuova sezione nel componente Home, sempre all'interno del Layout, in modo che l'elenco venga visualizzato al di sotto della parte introduttiva:

Il risultato dovrebbe essere:

Rotte dinamiche

Lista dei percorsi
Crea un file [id].js dentro a pages/posts: le pagine che iniziano e finiscono con le parentesi quadre sono rotte dinamiche in NextJS.
e rimuovi pure primo-post.js, che non ci servirà più.

Al suo interno inserisci questo codice:
import Layout from '../../components/layout';

export default function Post() {
  return <Layout>...</Layout>;
}

Questa sarà una pagina come le altre, solo che sarà richiamata con l'id del post desiderato, come vedremo dopo.

Apri adesso lib/posts.js e aggiungi questa nuova funzione in fondo al file:
export function getAllPostIds() {
  const fileNames = fs.readdirSync(postsDirectory);

  // Restituisce un array di questo tipo:
  // [
  //   {
  //     params: {
  //       id: 'ssg-ssr'
  //     }
  //   },
  //   {
  //     params: {
  //       id: 'pre-rendering'
  //     }
  //   }
  // ]
  return fileNames.map((fileName) => {
    return {
      params: {
        id: fileName.replace(/\.md$/, ''),
      },
    };
  });
}

Questa funzione legge tutti i nomi dei file nella cartella posts e prepara un array di oggetti contenenti gli id dei vari post.

#todo

Torna al file pages/posts/[id].js e importa il metodo appena creato.
Prendi spunto dall'importazione di getSortedPostsData fatta nella pagina index e... occhio al percorso!

Aggiungi nella pagina [id] il seguente metodo:

Esso sarà richiamato al momento del pre-rendering e restituirà l'elenco dei percorsi di tutti i post presenti nel blog... per farlo userà il metodo getAllPostIds che abbiamo creato e importato.


Implementare getStaticProps
Come abbiamo fatto nella pagina index, dobbiamo creare una funzione getStaticProps che sarà utilizzata al momento del Pre-rendering per riempire la pagina di ogni post con i dati presi dal suo file markdown.

Apri lib/posts.js e aggiungi questa funzione:
export function getPostData(id) {
  const fullPath = path.join(postsDirectory, `${id}.md`);
  const fileContents = fs.readFileSync(fullPath, 'utf8');

  // Use gray-matter to parse the post metadata section
  const matterResult = matter(fileContents);

  // Combine the data with the id
  return {
    id,
    ...matterResult.data,
  };
}

Essa, dato l'id di un post, ne apre il file e usa gray-matter per estrarre i metadati.
Apri pages/posts/[id].js e sostituisci questa riga:

...con queste:

Nota che abbiamo modificato l'import per importare entrambe le funzioni dal file lib/posts.

Abbiamo poi implementato il famoso metodo getStaticProps che sarà usato al momento del pre-rendering e fornirà al componente Post le props con i dati del singolo post.
Facciamo ora in modo che Post usi i dati ricevuti, riscrivendolo in questo modo:


Prova ora a visitare le pagine dei nostri due post:
http://localhost:3000/posts/ssg-ssr
http://localhost:3000/posts/pre-rendering

...in apparenza niente di eccezionale, ma prova a pensare come sono state costruite: tu hai scritto l'articolo in formato md inserendo solo titolo, data e testo... tutto il resto lo ha fatto NextJS, e la cosa bella è che adesso che è tutto pronto lo farà anche per gli altri 100 articoli che inserirai in quella cartella.
L'altra cosa bella è che se un giorno vorrai cambiare il layout del sito, non avrai bisogno di modificare le pagine di tutti gli articoli, perché si aggiorneranno da sole!

Ecco in sintesi quello che abbiamo fatto:

Renderizzare il markdown
Per fare il render del markdown di ogni post, e cioè trasformare i file md in html con grassetti, ecc. usiamo la libreria remark:

npm install remark remark-html

Importala nel file lib/posts.js, sotto all'importazione di gray-matter:

import { remark } from 'remark';
import html from 'remark-html';

Aggiorna il metodo getPostData in modo che oltre ai metadati estragga anche il testo del post convertendolo in HTML:
Nota: abbiamo aggiunto anche la parola chiave async che ci permetterà di caricare i dati in modo asincrono.

Aggiorna il file pages/posts[id].js in modo che usi await in coppia con l'async che abbiamo appena scritto:

e modifica Post in modo che visualizzi anche il contenuto del post, aggiungendo nel posto giusto questa riga:
<div dangerouslySetInnerHTML={{ __html: postData.contentHtml }} />



Rifinire la pagina del post
Nel file [id].js importa il componente Head e modifica Post come suggerito dai commenti:

Per formattare le date installiamo questa libreria:

npm install date-fns

Crea un componente Date inserendolo in un nuovo file da denominare in modo opportuno, e da inserire nella cartella opportuna:
import { parseISO, format } from 'date-fns';

export default function Date({ dateString }) {
  const date = parseISO(dateString);
  return <time dateTime={dateString}>{format(date, 'd MMMM yyyy')}</time>;
}

Importa il componente appena creato dentro a [id].js e usalo per formattare la data del post:
<Date dateString={postData.date} />

#todo

Provate a scoprire come cambiare il formato della data in modo che mostri anche il giorno della settimana, ad es. monday.

Aggiungiamo infine gli stili che abbiamo preparato nel modulo utils, modificando così il componente Post:

export default function Post({ postData }) {
  return (
    <Layout>
      <Head>
        <title>{postData.title}</title>
      </Head>
      <article>
        <h1 className={utilStyles.headingXl}>{postData.title}</h1>
        <div className={utilStyles.lightText}>
          <Date dateString={postData.date} />
        </div>
        <div dangerouslySetInnerHTML={{ __html: postData.contentHtml }} />
      </article>
    </Layout>
  );
}

Ecco come dovrebbe apparire la pagina di un post:

Rifinire la pagina index
Aggiorniamo la nostra pagina index.js in modo che contenga i link ai singoli post.
Per prima cosa importa il componente Link come avevamo fatto in Layout, e il componente Date creato poco fa.

Sostituisci quindi il tag <li>...</li> con il seguente:

Prova ad andare su index e verifica che i post siano raggiungibili dagli opportuni link.


MateriaUI

Installazione
MUI permette di creare delle splendide interfacce utente.
Sito ufficiale:
https://mui.com/

Installazione

Con le righe che seguono installiamo i componenti di MUI per poterli usare nella nostra applicazione:

npm install @mui/material @emotion/react @emotion/styled
npm install @fontsource/roboto
npm install @mui/icons-material

 Vengono installati anche il font Roboto e le icone, che di solito sono utili in qualsiasi progetto.

Inserire un componente
Per usare un componente di MUI bisogna prima importarlo nella pagina in cui vogliamo usarlo (ad esempio index.js):

Possiamo poi inserirlo dove vogliamo in questo modo:

Nella guida di MUI troviamo una pagina completa di esempi che spiega nel dettaglio il componente:
https://mui.com/material-ui/react-button/


Premendo il bottone Show the full source possiamo vedere l'esempio completo. Ci interessano in particolare le righe "import":

Gli esempi di solito mostrano come inserire il componente nel contesto di una pagina, ad esempio qui sopra vediamo che tre bottoni sono inseriti in un componente Stack.

#todo

Prova a creare un file per il componente BasicButtons col codice visto sopra (non serve la riga che importa React) e a importarlo nella tua pagina index.


Container e Box

Box

Viene usato come wrapper (involucro) per una serie di utility CSS che si vogliono applicare.
La prop sx permette di specificare le varie proprietà CSS:
    <Box
      sx={{
        width: 300,
        height: 300,
        backgroundColor: 'primary.dark',
        '&:hover': {
          backgroundColor: 'primary.main',
          opacity: [0.9, 0.8, 0.7],
        },
      }}
    />

Container fluid

È il classico container che conosciamo già da Bootstrap.
La proprietà maxWidth fissa la larghezza massima del container: per schermi più stretti esso si restringe in maniera fluida.

<Container maxWidth="sm">
  <Box sx={{ bgcolor: '#cfe8fc', height: '100vh' }} />
</Container>

Container fixed

A differenza del precedente, al diminuire della grandezza dello schermo il container si ridimensiona a scatti fissati dai breakpoint:

<Container fixed>
  <Box sx={{ bgcolor: '#cfe8fc', height: '100vh' }} />
</Container>

Grid
Anche MUI utilizza di default una griglia a 12 colonne.

Il componente viene usato come contenitore (attributo container = true), come elemento contenuto (attributo item = true) o entrambi.
Ecco un semplice esempio:

      <Grid container spacing={2}>
        <Grid item xs={8}>
          <Item>xs=8</Item>
        </Grid>
        <Grid item xs={4}>
          <Item>xs=4</Item>
        </Grid>
        <Grid item xs={4}>
          <Item>xs=4</Item>
        </Grid>
        <Grid item xs={8}>
          <Item>xs=8</Item>
        </Grid>
      </Grid>

Esso produce il seguente layout:


Possiamo specificare la larghezza che un elemento (item) deve avere per un certo breakpoint e per quelli di dimensioni maggiori, che nell'ordine sono:
  • xs = extra small
  • sm = small
  • md = medium
  • lg = large
  • xl = extra large

Possiamo definire larghezze multiple, facendo in modo ad esempio che un elemento sia largo 6/12 su schermi extra piccoli (xs) e piccoli (sm) e 4/12 su schermi dal medium (md) in sù:
xs={6} md={4}

L'attributo spacing (che potrebbe essere inserito anche in un sx) specifica la spaziatura tra gli elementi:

<Grid container spacing={12}>

AppBar

#todo

Scegliete uno degli esempi del componente AppBar di MUI e usatelo per creare nella cartella components un componente BarraSuperiore.
Usate poi questo componente all'interno del componente Layout per far comparire la barra di navigazione su tutte le pagine del sito.

Il risultato dovrebbe essere il seguente:

Il metodo .map()
In questo esempio viene creato un nuovo array quadrati prendendo ogni numero e calcolandone il quadrato.

Questa operazione viene chiamata mappatura:

A .map() viene passata come argomento una funzione che restituisce il quadrato del numero (num*num).

 

In questo altro esempio mappiamo ogni nome con una versione dello stesso nome con l'iniziale maiuscola:

Nota: viene passata una funzione anonima usando la sintassi delle funzioni "freccia".

Mapping di array
Possiamo sfruttare il metodo .map per creare una serie di bottoni.
Apri il file di una pagina a tua scelta (o creane una nuova) e crea dopo la sezione import un array con i nomi dei bottoni:

Crea una nuova sezione che usa il metodo map per mappare ogni stringa in un list item (li):

Come vedi map prende ogni singolo elemento dell'array bottoni e lo mette nella variabile temporanea nome.
Sarà quindi possibile usare nome per creare il list item corrispondente.

Trasformiamo ora i list item nei rispettivi bottoni, inseriti in un componente Stack di MUI (ricordati di importarlo):





Creazione di componenti

Preparativi
Importiamo nel nostro componente Layout il componente CSS Baseline di MUI, che serve a preparare una base di partenza di stili CSS eleganti e coerenti:
https://mui.com/material-ui/react-css-baseline/

Importiamo inoltre il componente ThemeProvider che ci permetterà in seguito di preparare diversi temi per il sito:
https://mui.com/material-ui/customization/theming/

Importiamo infine il componente Container, nel quale inseriremo per il momento tutto il contenuto delle pagine:
https://mui.com/material-ui/react-container/


Creiamo un tema vuoto:

Incapsuliamo tutto il layout dentro al ThemeProvider, e inseriamo CssBaseline in modo che venga usato da tutte le pagine del sito:

Inseriamo infine tutto il contenuto della pagina in un container:

Header
Nel componente Layout creiamo un array di oggetti contenenti i link alle varie sezioni del sito... lo faremo poi visualizzare nel menù principale dell'header:


Importiamo il nostro componente Header e inseriamo nel Layout passandogli il titolo del sito e la lista delle sezioni:

Andiamo adesso a lavorare nel file del componente Header...
Importiamo alcuni componenti di MUI che ci serviranno:

Facciamo in modo che il componente riceva le props: titolo e sezioni.
Facciamo pulizia nel blocco return e prepariamo un React.Fragment:
https://reactjs.org/docs/fragments.html
Il Fragment permette di restituire diversi componenti raggruppati.

Restituiamo una prima Toolbar contenente una prima fascia con dei bottoni, del testo, un'icona:

Restituiamo inoltre una seconda Toolbar contenente i link alle sezioni del sito:
Nota come abbiamo usato il metodo map per creare i diversi Link partendo dall'array sezioni.

Per approfondire:
https://mui.com/material-ui/react-typography/
https://mui.com/material-ui/react-link/
https://mui.com/material-ui/react-button/
https://mui.com/material-ui/react-app-bar/

Post in evidenza
Prepariamo un esempio di post/articolo/notizia che venga messo in evidenza sull'intera larghezza della pagina.

Per prima cose definiamo nella pagina index un oggetto JavaScript con tutti i dettagli del post:

Lo passeremo come props del componente che andremo a creare:
Ricordati di importare il componente.

Definiamo quindi il nostro nuovo componente, importando i componenti necessari:

Tutto sarà racchiuso in un componente Paper:

Sopra l'immagine ci sarà un layer nero con un'opacità del 40%, per facilitare la lettura dei testi:

Il testo sarà posizionato solo su metà (6/12) dell'immagine (per gli schermi dal large in sù) grazie a un componente Grid:


Per approfondire:
https://mui.com/material-ui/react-paper/
https://mui.com/material-ui/react-box/
https://mui.com/material-ui/react-grid/

Post
Creiamo un array di oggetti contenenti i dati dei post:

Inseriamo un componente Grid che ospiterà i post nella pagina index:

Definiamo il componente Post, che userà i seguenti componenti di MUI:

Esso è composto da un componente Grid item contenente un'area cliccabile dentro la quale abbiamo il testo e l'immagine della Card:


Per approfondire:
https://mui.com/material-ui/react-card/

Carousel
Installiamo il componente con
npm install react-material-ui-carousel --save

Definiamo nella pagina index un array con i dati delle slide:

Importiamo il nostro componente Carousel passandogli le slide:

Definiamo il componente Carousel importando i componenti necessari, oltre ad un foglio di stile:

Denominazione dei componenti

Denominiamo il componente MyCarousel per distinguerlo da quello che importiamo nella prima riga, e che ci servirà da base.
Nota però che nella pagina index abbiamo importato MyCarousel denominandolo semplicemente Carousel.

Ecco la struttura del componente:

Che darà questo risultato:

Il foglio di stile è questo:

Transizioni

È possibile cambiare il tipo di transizione agendo su questi attributi del componente che abbiamo importato:

Esempio tratto da
https://codesandbox.io/s/materialui-carousel-p01dc?file=/src/ProjectCarousel.scss:20-494

Fetch

Prefetch

API pubblica del Cinema

Vogliamo interfacciarci all'API pubblica del sito del Cinema don Bosco.

Endpoint che restituisce la lista di tutti i film in programmazione:
https://cinema.donboscosandona.it/movie/featured.json

Endpoint che restituisce i dati completi del singolo film:
https://cinema.donboscosandona.it/movie/show/316.json
(nell'esempio passiamo l'id del film 316).


Lettura dei dati da React

Nella nostra pagina andiamo a leggere (fetch) i dati (data) e li passiamo al componente Page nella fase di build, usando getStaticProps come abbiamo già fatto in precedenza per i post del blog.

Nella pagina compare il componente Movies, che riceverà i dati (data) e disegnerà una griglia di card:

Ogni card è disegnata usando questo componente MovieCard, che riceve i dati del singolo film:


Il risultato sarà:

Fetch lato client
Vogliamo provare a fare il fetch lato client, e cioè ogni volta che l'utente apre la pagina.

Vantaggi e svantaggi

Il visitatore vedrà sempre i dati più aggiornati, ma questo richiederà un maggiore tempo di esecuzione.

Useremo SWR:

npm install swr

Il componente principale Movies  leggerà i dati dall'API e costruirà una griglia con le singole card.
Potremo quindi evitare di passargli i dati via props.



I nostri componenti

Introduzione
Lo scopo è realizzare una collezione di componenti React che ci permettano poi di costruire facilmente il sito della scuola e molti altri ancora.

Progetto Open Source

Essendo il nostro un progetto open source nulla vi vieterà di usare un giorno questa collezione di componenti per realizzare dei siti per voi e i vostri clienti.


Preparativi



Requisiti per l'accettazione delle pull request

  • corretta denominazione del componente e dei file
  • corretta denominazione e passaggio delle props al componente
  • uso di componenti MUI e non generici (ad es. Container, Grid, Box e non semplici div)
  • corrispondenza del layout a quello richiesto
  • corrispondenza dei font
  • assenza di file esterni alla propria cartella dev
  • nomi dei file (ad es. immagini) tutti in minuscolo


Pagine di prova

Nella cartella /pages/dev (development) create una cartella con il nome breve della vostra organizzazione e lì dentro create una pagina index.js (più eventuali altre pagine che vorrete creare):

In queste pagine potrete provare a usare i componenti che creerete, per vedere come appaiono nel contesto del layout generale del sito.

Denominazione e posizione dei componenti

Anche nella  cartella /dev create una sottocartella con il nome della vostra organizzazione e lì dentro create tutti i vostri componenti:

Denominazione dei componenti

Il nome del componente deve corrispondere a quello specificato nel listino qui su books.
Il nome del file deve essere uguale a quello del componente (maiuscole comprese), con l'aggiunta dell'estensione .js

Link utili

La pagina index della vostra organizzazione sarà raggiungibile da un indirizzo del tipo:

http://localhost:3000/dev/dottorgross

Il branch main (quello dove il prof farà il merge delle vostre pull request) sarà sempre visibile su
https://donboscosandona.vercel.app/

Quando saremo pronti sostituiremo con il nostro l'attuale sito della scuola:
https://www.donboscosandona.it/

LandingHero
Componente sviluppato da Filippo Bagnoli, Valentina Segato, Elia Barbaric, Lorenzo Alberti, 2F 2022-23

Useremo questo componente nella home page: oltre a un'immagine e a un testo d'impatto conterrà un bottone che permetterà di raggiungere una pagina da mettere in evidenza.
In alto a sinistra comparirà la scritta siteName, in alto a destra una serie di link che porteranno alle sezioni principali del sito.
All'immagine sarà sovrapposta una schermatura nera della quale sarà possibile regolare l'opacità con l'apposito attributo.

let menu = [
    { title: 'Informatico', url: '/informatico' },
    { title: 'Energie', url: '/energie' },
    { title: 'Elettrico', url: '/elettrico' },
    { title: 'Meccanico', url: '/meccanico' },
    { title: 'Motoristico', url: '/motoristico' },
    { title: 'Carrozzeria', url: '/carrozzeria' },
    { title: 'Trasparenza', url: '/trasparenza' },
]

<LandingHero
  siteName="CFP DON BOSCO"
  title="Your Story Starts With Us."
  description="Every landing page needs a small description......."
  imageUrl="https://url.dell.immagine"
  opacity={ 0.7 }
  buttonText="WATCH VIDEO"
  buttonUrl="https://....."
  menu={ menu }
  height={ 80 }
/>

I parametri buttonText, buttonUrl e menu sono opzionali


Carousel
Definiamo nella pagina index un array con i dati delle slide:
let slides = [
  {
    titolo: 'Fatti un bel giro',
    descrizione: 'Scopri tutti i segreti del nostro Centro con il tour virtuale!',
    immagine: 'https://source.unsplash.com/random',
    colore: "#ED4C67",
    opacity: 0.5,
    blur: "0.5rem",
    buttonText: 'Scopri di più!',
    buttonUrl: 'https:...',
  },
  {
    titolo: 'Concorso nazionale settore elettrico',
    descrizione: "A maggio il nostro Centro avrà l'onore di ospitare il Concorso Nazionale del Settore Elettrico: tutti i Centri di Formazione Professionale salesiani d'Italia invieranno i loro campioni per una settimana di sfida e condivisione professionale...",
    colore: '#22aa22',
    colore2: 'transparent', // opzionale, per realizzare gradienti, può essere un rgba e viene comunque usata anche opacity
  },
]

buttonText e buttonUrl sono facoltative;
se buttonUrl non è presente il bottone non viene visualizzato.

Importiamo il nostro componente Carousel passandogli le slide e la larghezza desiderata (false fa sì che il carousel si estenda all'intero schermo):

<Carousel
  slides={slides}
  maxWidth={false}
  height="90"
>

Features
Componente sviluppato da Alessio Solighetto e Daniel Solighetto, 2F 2022-23

Mette in evidenza i nostri punti di forza. Riceve un titolo, una descrizione e un array contenente un oggetto per ogni punto di forza.
L'attributo cardWidth specifica la larghezza delle singole colonne... nell'esempio sopra abbiamo tre colonne che occupano ognuna i 4/12 della larghezza della pagina.
Se l'array features dovesse contenere un numero maggiore di oggetti, questi dovrebbero andare a capo.

let features = [
  {
     imageUrl: "https....",
     title: "Free Chat",
     description: "Divide details about..."
  },
  ...
]

<Features
  title="Why our product is the best"
  description="This is the paragraph......."
  features={ features }
  cardWidth={ 4 }
/>

Team
Componente sviluppato da Filippo Urban e Zaccaria Cesaro, 2F 2022-23
Presenta i membri del team. Riceve un titolo, una descrizione e un array contenente un oggetto per ogni membro.
L'attributo cardWidth specifica la larghezza delle singole colonne... nell'esempio sopra abbiamo quattro colonne che occupano ognuna i 3/12 della larghezza della pagina.
Se l'array members dovesse contenere un numero maggiore di oggetti, questi dovrebbero andare a capo.

let members = [
  {
     name: "Alec Thompson",
     role: "CEO / CO-FOUNDER",
     description: "And I love you like...",
     imageUrl: "https..."
     linkedinUrl: "https...",
     facebookUrl: "https...",
     instagramUrl: "https...",
     maxWidth: "lg"
  },
  ...
]

<Team
  title="Our Awesome Team"
  description="This is the paragraph......."
  members={ members }
  cardWidth={ 3 }
/>



Products
Componente sviluppato da Lorenzo Alberti e Elia Barbaric, 2F 2022-23

Presenta una serie di prodotti in evidenza. Riceve un titolo, una descrizione e un array contenente un oggetto per ogni prodotto.
L'attributo cardWidth specifica la larghezza delle singole colonne... nell'esempio sopra abbiamo tre colonne che occupano ognuna i 4/12 della larghezza della pagina.
Se l'array products dovesse contenere un numero maggiore di oggetti, questi dovrebbero andare a capo.

let products = [
  {
     title: "NextJS Material Kit Free",
     category: "FREE UI KIT",
     description: "It is a Fre Material-UI Kit with.....",
     immagineUrl: "https:...",
     url: "https..."
  },
  ...
]

<Products
  title="Some of Our Awesome Products"
  description="This is the paragraph......."
  products={ products }
  cardWidth={ 4 }
  borderRadius="10px"
  aspectRatio="3 / 2"
/>

Testimonials
Componente sviluppato da Alessio Solighetto e Daniel Solighetto, 2F 2022-23

Presenta una serie di recensioni da parte dei clienti. Riceve un titolo, una descrizione e un array contenente un oggetto per ogni recensione.
L'attributo cardWidth specifica la larghezza delle singole colonne... nell'esempio sopra abbiamo tre colonne che occupano ognuna i 4/12 della larghezza della pagina.
Se l'array testimonials dovesse contenere un numero maggiore di oggetti, questi dovrebbero andare a capo.

let testimonials = [
  {
     name: "Alec Thompson",
     channel: "@Google",
     text: "Your produtcs, all the kits.......",
     imageUrl: "https..."
  },
  ...
]

<Testimonials
  title="Our Clients Love Us"
  description="You need more informations?....."
  imageUrl="https....."
  opacity={0.7}
  testimonials={ testimonials }
  cardWidth={ 4 }
/>


Footer
Componente sviluppato da Bilal Amara, Thiago Alves Rodrigues e Dionise Rotaru, 2F 2022-23

Footer che apparirà sul fondo di tutte le pagine del sito. Sarà possibile passare al componente un'immagine di sfondo opzionale oppure un colore di sfondo. L'attributo opaciy permetterà di sovrapporre all'immagine un Box con il colore di sfondo e un'opacità regolabile.
La prima colonna sarà destinata a un testo descrittivo, la seconda ai social, la terza a un menù di navigazione grafico (che porta ad es. ai settori).
Sul fondo compariranno un ulteriore menù personalizzabile e una nota di copyright.

let socials = [
  { title: 'Facebook', imageUrl: 'https...', url: '...' },
  { title: 'Instagram', imageUrl: 'https...', url: '...' },
  { title: 'Youtube', imageUrl: 'https...', url: '...' },
  { title: 'Linkedin', imageUrl: 'https...', url: '...' },
]

let images = [
  { title: 'Informatico', imageUrl: 'https...', url: '...' },
  { title: 'Informatico', imageUrl: 'https...', url: '...' },
  { title: 'Informatico', imageUrl: 'https...', url: '...' },
]

let menu = [
    { title: 'BLOG', url: '/informatico' },
    { title: 'PRESENTATION', url: '/energie' },
    { title: 'DISCOVER', url: '/elettrico' },
    { title: 'PAYMENT', url: '/meccanico' },
    { title: 'CONTACT US', url: '/motoristico' },
]

<Footer
  imageUrl="https://immagine di sfondo"
  color="#777"
  opacity={1}
  title1="About Us"
  description1="Creative Tim is a startup..."
  title2="Social Feed"
  socials={socials}
  title3="I Settori"
  images={images}
  menu={menu}
  copyright="Copyright (C) 2023 9dreams Agency."
/>

Paragraph

Un paragrafo di testo inserito in un container, con un colore di sfondo opzionale e un'immagine collegata.

<Paragraph
  title="Risotto con gli asparagi"
  subtitle="Una specialità tutta nostrana"
  topImageUrl="https..."
  avatarImageUrl="https..."
  leftImageUrl="https..."
  rightImageUrl="https..."
  backgroundImageUrl="https..."
  backgroundColor="#225533"
  opacity={0.8}
  blur="0.2rem"
  color="white"
  columnCount={1}
  maxWidth="lg"
  sx={{...}}
>
  Qui va tutto il testo del paragrafo...
</Paragraph>

Gli attributi sono tutti opzionali.

Maps
Componente sviluppato da Filippo Bagnoli e Valentina Segato, 2F 2022-23
<Maps
  maxWidth='100%'
  maxHeight='550px'
  url="https://www.google.com/maps/embed?pb=!1m14!1m8!1m3!1d5580.05343317009!2d12.5710658!3d45.6301996!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x477956fe076b4157%3A0x29fb231d47465883!2sCnos%20Fap%20Don%20Bosco!5e0!3m2!1sit!2sit!4v1680507660807!5m2!1sit!2sit"
/>

Table

      <Table
        title="Titolo della tabella"
        subtitle="Sottotitolo della tabella"
        backgroundImageUrl="https..."
        backgroundColor="#EE5A24"
        opacity={0.8}
        blur="0.2rem"
        color="white"
        elevation="1"
        rows={[
          ["Cognome", "Nome", "Età"],
          ["Paoletti", "Vigilio", 75],
          ["Scaroletti", "Emma", 45],
          ["Anselmi", "Ada", 32],
        ]}
      />

Calendar
<Calendar title="Prima settimana" cardWidth={3} events={settimana1} />

let settimana1 = [
  {
    date: "Lunedì 19 Giugno",
    morning: "Attività regolare",
    afternoon: "Giochi al parco e tornei",
    evening: "ore 20.45 Film TRANSFORMERS - IL RISVEGLIO",
    immagineUrl: "/images/calendario/transformers.png",
  },
];

Props opzionali:
  aspectRatio="3 / 2" // proporzioni di ritaglio delle immagini

News
      <News title={null} data={data} />

Props opzionali:
  limit={6} // numero massimo di post presentati
  aspectRatio="3 / 2" // proporzioni di ritaglio delle immagini


È possibile usare SwiperNews al posto di News, con le stesse props: viene visualizzato come slider.
Entrambi fanno usp di un componente NewsCard apposito.

Ricevono i dati via API dal backend channels.

Featured

      <Featured data={data} />

Featured si appoggia al componente Carousel.
Props opzionali:
  limit: 6 // numero massimo di post presentati
  maxWidth: false // larghezza del container
  height: 90 // altezza del container

Sponsor

let sponsor = [
  'https://agsol.com/wp-content/uploads/2018/09/new-microsoft-logo-SIZED-SQUARE.jpg',
  'https://assets.ubuntu.com/v1/57a889f6-ubuntu-logo112.png',
  'https://upload.wikimedia.org/wikipedia/commons/thumb/0/08/Cisco_logo_blue_2016.svg/1200px-Cisco_logo_blue_2016.svg.png',
  'https://dev.socialidnow.com/images/9/94/Mikrotik-logo.png',
  'https://www.raspberrypi.org/app/uploads/2018/03/RPi-Logo-Reg-SCREEN.png',
  'https://www.arduino.cc/en/uploads/Trademark/ArduinoCommunityLogo.png',
  'https://images-eu.ssl-images-amazon.com/images/I/413W%2BhcdyEL.png',
  'https://www.comitec.it/img/logo.png?v=1.1',
  'https://archive.donboscosandona.it/img/ck/1e0a315dbf7a64beb118a36bbc2148c8d20f55a3.png',
]

<Sponsor title='I nostri sponsor' logos={sponsor} />

React Refresher da corso NextJS

Introduzione
npm start




Le basi



Stili in index.css

I vantaggi di scomporre in componenti.

Props

Gestione dello stato

Visualizzazione condizionale

Router


Link

Moduli CSS: prendere l'abitudine di mettere gli stili nel modulo, come con React Native


Render di array di oggetti

Layout come componente wrapper: props.children





Form

Firebase offre un database e le API per interrogarlo.



In test mode.


Facciamo una richiesta http di tipo POST. Potremmo usare axios che è una libreria di uso molto comune, ma qui lo facciamo col fetch.
Fetch di default fa richieste GET.

L'url viene trattato come una cartella / una tabella:

I dati inviati sono visibili su Firebase:

Dopo aver fatto il post torniamo alla home:

Fetching data

Stesso URL, ma stavolta con GET.



Ciclo infinito: cambia lo stato e rifà il fetch e via così.
useEffect permette di eseguire codice sotto certe condizioni.
Primo argomento: una funzione che contiene il codice da eseguire
Secondo argomento: array di dipendenze che vengono valutate... il codice viene eseguito solo se il loro valore cambia. Se è vuoto React esegue la funzione solo la prima volta. Le dipendenze sono i valori esterni alla funzione da cui essa dipende... se non cambiano non viene rivalutata.


Firebase non restituisce un array ma un oggetto che ha come proprietà gli id degli elementi:

Non possiamo usare semplicemente il .map()

Context
Vogliamo usare lo stato in diversi componenti.
Lift State: lo portiamo a livello di App.js e poi passiamo le parti necessarie come props ai singoli componenti.
Per applicazioni grandi il componente App diventa sempre più grande, e abbiamo catene di stato molto lunghe.

Redux è un package per la gestione dello stato, ma React stesso ha una soluzione per la gestione dello stato a livello dell'intera applicazione: Context

Creiamo una cartella /store

I componenti racchiusi nel Context hanno accesso ai dati del Context.
















Stato

Componente Test