Vytvořte aplikaci pro předplatné e-mailu – Část 1:Připravte naši komponentu React

Přehled

Všichni víme o úspěchu a růstu ranního zpravodaje miláčku Morning Brew. Společnost Morning Brew, kterou založil Alex Leiberman, nyní získává více než 25 milionů předplatitelů a nedávno prodala většinovou část svého podnikání za 70 milionů dolarů společnosti Business Insider. Celé podnikání je kurátorské předplatné e-mailového marketingu, které každé ráno doručuje přizpůsobený e-mail „zrychlení“. Sami jako vedoucí pracovníci můžeme říci, že je to skvělé.

Inspirováni jednoduchostí tohoto obchodního modelu jsme se rozhodli použít službu předplatného e-mailu podobnou Morning Brew jako základ našeho výukového programu, který předvádí sílu, flexibilitu a škálovatelnost Zustand, střední a štíhlé technologie správy stavu, která by měla být nový přírůstek do každé sady nástrojů frontend vývojáře.

Tento tutoriál je třídílnou sérií a poskytne podrobný návod, jak provádět správu stavu na úrovni komponent pomocí Zustand. Ukážeme si, jak používat Zustand takticky relevantním způsobem při vytváření plně integrované komponenty React.

Zde je rozpis toho, co budeme pokrývat v celé sérii:

  • Část 1A:Vytvoření základní komponenty React
  • Část 1B:Přidání nakladače kostry
  • Část 2:Připojení Zustand pro řízení stavu tekutin
  • Část 3:Propojení backendové logiky a datových zdrojů mikroslužeb

Jako upozornění, každá výše uvedená část je pro pohodlí propojena s kódovou karanténou, doplněnou o plně dokončený kód sekce. Abyste svůj čas při sledování tohoto návodu využili co nejlépe, doporučujeme otevřít a rozvětvovat sandbox součásti na začátku sekce na samostatné kartě. Náš příklad Code Sandbox může být vaším „cílovým repozitářem“. Zatímco dokončujete každou část tutoriálu, vaším cílem by mělo být napsat kód, který se nakonec bude podobat cíli.

Předpoklady

Pro absolvování tohoto kurzu je nutná základní znalost React a Node.js.

V části 3 výukového programu budete také potřebovat účet Buildable Developer a účet SendGrid (nebo jiné e-mailové rozhraní API dle výběru).

Náš cíl

Abychom předvedli Zustand, vytvoříme repliku webové aplikace Morning Brew. Celá stavba může být dokončena za 20 minut nebo méně, a co je důležitější, bude plně propojena. To znamená, že komponenta React naší aplikace bude mít plynulé přechody stavů, bude připojena k mikroservisní síti (kterou spustíte) pro ukládání shromážděných potenciálních zákazníků (tj. e-mailů) a bude mít plně nasazený backend pro spouštěče e-mailů. Ke komponentě React dokonce přidáme zavaděč kostry, který ji upraví při načítání dat!

Když se dostaneme k připojení dynamických dat ke komponentě (část 3 tutoriálu), zjistíte, že pro připojení používáme Buildable Recipe. To je důležité, protože chceme zajistit, aby se naši vývojáři vyhnuli co největšímu počtu nasazení, když tuto komponentu přesuneme do výroby. V produkci je DevOps vždy zlomovým bodem pro většinu inženýrských týmů, takže pomocí těchto nástrojů budeme pracovat na tipech a tricích, které přesně předvedou, jak tento proces vyhladit a udržet naše procesy nasazení jednoduché.

Zde je vstupní stránka Morning Brew, která shromažďuje e-maily a odesílá potvrzovací e-mail odběrateli:

Takto bude vypadat naše replika Morning Brew:

Takto bude vypadat naše replika Morning Brew:

Na obrázku výše můžete vidět, jak uživatel odesílá svůj e-mail pro přihlášení k odběru newsletteru. Při odeslání uživatelem uložíme e-mail tohoto uživatele a spustíme doručení uvítacího e-mailu stejnému uživateli. Začněme.

Začínáme:Nastavení projektu

Budeme předpokládat, že začínáte z prázdné aplikace React (vytvořené pomocí create-react-app), takže začněme tam.

Instalace balíčků

První věc, kterou musíme udělat, je nastavit náš projekt. Nejprve nainstalujeme následující balíčky:

  • @material-ui/core (používá se pro formuláře, tlačítka atd..)
  • @material-ui/lab (používá se pro skeleton loader)
  • průchodka (používá se pro celkové rozvržení a typografii)
  • styled-components
  • barva (používá se pro ovládání barvy tlačítka)
npm i @material-ui/core @material-ui/lab grommet styled-components color

Uspořádání struktury složek

Připravíme se na úspěch s čistou a uspořádanou strukturou složek, která je následující:

Vložení motivu aplikace

V zájmu konzistence vložíme do theme.js téma pro celou aplikaci soubor.

export default {
  colors: {
    primary: "#0015F7",
    secondary: "#FF3D57",
    red: "#FF4100",
    yellow: "#FFAB00",
    blue: "#536DFF",
    green: "#00D067",
    black: "#171A1D",
    white: "#ffffff",
    purple: "#3a0ca3",
    "grey-1": "#FCFCFC",
    "grey-2": "#EDEDED",
    "grey-3": "#C4C4C4",
    "grey-4": "#8296A9",
    "custom-light-blue": "#40a9ff",
    "custom-blue": "#F5F9FA",
    "custom-light-green": "#E4F9EA",
    "light-blue": "rgba(0, 99, 237, 0.04)"
  },
  edgeSize: {
    none: "0px",
    hair: "1px",
    xxsmall: "3px",
    xsmall: "6px",
    small: "8px",
    "medium-smaller": "14px",
    "medium-small": "18px",
    medium: "24px",
    "medium-large": "32px",
    large: "48px",
    xlarge: "96px"
  },
  sizes: {
    xxxsmall: "48px",
    xxsmall: "96px",
    xsmall: "192px",
    small: "384px",
    medium: "600px",
    large: "768px",
    xlarge: "1152px",
    xxlarge: "1536px",
    full: "100%",
    "custom-x-small": "160px"
  },
  fontSizes: {
    xsmall: 10,
    small: 12,
    medium: 14,
    large: 16
  }
};

Založení komponenty React

Nyní, když jsme všichni připraveni, začneme pokládat základy naší komponenty. Začněme tím, že zamíříme do components/email-block a vytvořením následujících souborů:Button.js , EmailBlock.js a index.js

Úprava stylu tlačítka

V zájmu jednoduchosti rychle upravíme styl tlačítka material-ui a zároveň budeme odpovídat vyššímu standardu estetiky, který hledáme. V Button.js , přidejte následující fragment kódu:

import React from "react";
import { Button } from "@material-ui/core";
import { makeStyles } from "@material-ui/core/styles";
import Color from "color";
import theme from "../../theme";

const { colors } = theme;

const borderStyle = {};

const useStyles = ({ style, color, background }) =>
  makeStyles(() => ({
    button: {
      textTransform: "none",
      fontSize: theme.fontSizes.medium,
      fontWeight: 600,
      boxShadow: "none",
      borderRadius: borderStyle["border-radius"],
      color,
      background,
      "&:hover": {
        boxShadow: "none",
        color:
          !background || background === "transparent"
            ? Color(color)
                .darken(10 / 100)
                .hex()
            : color,
        background:
          background && background !== "transparent"
            ? Color(background)
                .darken(5 / 100)
                .hex()
            : "transparent"
      },
      "&:disabled": {
        background: colors["grey-3"],
        pointerEvents: "all !important",
        "&:hover": {
          cursor: "not-allowed !important",
          background: colors["grey-3"]
        }
      },
      ...style
    }
  }))();

const MatButton = ({
  children,
  onClick = () => {},
  variant = "text",
  color = colors.black,
  background = "transparent",
  style = {},
  ...props
}) => {
  if (typeof children !== "string") {
    throw new Error("MatButton received children that is not string");
  }
  const classes = useStyles({ style, color, background });
  return (
    <Button
      variant={variant}
      className={classes.button}
      onClick={onClick}
      {...props}
    >
      {children}
    </Button>
  );
};

export default MatButton;

Vytvoření e-mailového bloku

Nyní vytvoříme a vyexportujeme funkční komponentu React karty, která se nakonec připojí k Zustand a bude spravovat stav komponenty. Tato složka bude středem naší pozornosti, když postoupíme do další fáze. V EmailBlock.js , přidejte následující fragment kódu:

import React from "react";

const EmailBlock = () => {
  return (
    <div>Our card</div>
  );
};

export default EmailBlock;

Import e-mailového bloku

Nyní pojďme k src/App.j s soubor. V tomto souboru jednoduše importujeme e-mailový blok a přidáme obrázek na pozadí. Také přidáme logo naší aplikace v src/assets/images . Zde je fragment kódu:

import { Box, Image } from "grommet";
import { makeStyles } from "@material-ui/core";
import React from "react";
import EmailBlock from "./components/email-block";
import "./styles.css";
import logo from "./assets/images/logo.svg";
import theme from "./theme";

const useStyle = makeStyles({
  root: {
    background:
      "url(https://images.unsplash.com/photo-1601933552406-c6ea0739a098?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=3300&q=80)",
    backgroundSize: "cover",
    backgroundRepeat: "no-repeat",
    backgroundPosition: "center center"
  }
});

export default function App() {
  const classes = useStyle();
  return (
    <Box
      width="100%"
      pad="xlarge"
      height="100vh"
      gap="large"
      className={classes.root}
    >
      <Image src={logo} width={300} />
      <EmailBlock />
    </Box>
  );
}

Sestavení základní struktury komponenty

Nyní se vrátíme k EmailBlock.js začít sestavovat strukturu komponenty React. Komponenta bude obsahovat název, titulky, vstupní pole a tlačítko. Budeme používat Grommetův nadpis, Grommetův text, Material-UI TextField a stylizované tlačítko, které jsme vytvořili dříve. V EmailBlock.js soubor, přidejte následující fragment kódu:

import React from "react";
import { Box, Text, Heading } from "grommet";
import { TextField } from "@material-ui/core";
import theme from "../../theme";
import Button from "./Button";

const { colors } = theme;

const EmailBlock = () => {
  return (
    <Box
      elevation={"large"}
      width={"500px"}
      height={{ min: "max-content" }}
      round="8px"
      background={colors.white}
      pad={"large"}
      gap={"medium"}
    >
      <Heading level={1} color={colors.black}>
        Become smarter in just 5 minutes
      </Heading>
      <Text size={"medium"}>
        Get the daily email that makes reading the news actually enjoyable. Stay
        informed and entertained, for free.
      </Text>
      <TextField
        id="outlined-basic"
        type={"email"}
        label={"Enter your email"}
        placeholder={"Enter your email"}
        variant="outlined"
      />
      <Button
        type="submit"
        onClick={(e) => {
          e.preventDefault();
        }}
        // disabled={isProcessing || !isValid}
        background={colors.primary}
        color={colors.white}
        style={{
          paddingTop: "16px",
          paddingBottom: "16px"
        }}
      >
        Submit
      </Button>
    </Box>
  );
};

export default EmailBlock;

V této fázi máme plně sestavenou základní strukturu naší komponenty React 👏 a naše aplikace nyní vypadá takto 👇

Vylepšení uživatelského rozhraní pomocí Skeleton Loader

Plánujeme používat Zustand a Buildable Recipe k dynamickému načítání obsahu této komponenty při vrstvení v backendové logice, takže bude velmi užitečné přidat do komponenty stav načítání. Skeletové nakladače jsou často jedním z nejjemnějších doplňků jakéhokoli uživatelského rozhraní. Přesto jsou zodpovědné za to, že uživateli poskytují příjemný zážitek, kdykoli musíme čekat na načtení dat. Máme rádi zdvořilosti, tak pojďme jeden přidat.

Zde je návod, jak bude komponenta vypadat při načítání dat pomocí zavaděče kostry:

Vytvoření načítacího bloku

Chcete-li přidat náš nakladač kostry, přejděte na src/components/email-block složku a přidejte LoadingBlock.js soubor. Do souboru přidejte následující fragment kódu:

import React from "react";
import { Box } from "grommet";


const LoadingBlock = () => {

  return (
    <Box gap={"medium"}>
      Loading
    </Box>
  );
};

export default LoadingBlock;

Nyní se vraťme k EmailBlock.js soubor pro přidání komponenty zatěžovacího bloku a vytvoření simulace zatěžování.

import React, { useEffect, useState } from "react";
import { Box, Text, Heading } from "grommet";
import { TextField } from "@material-ui/core";
import theme from "../../theme";
import Button from "./Button";
import LoadingBlock from "./LoadingBlock";

const { colors } = theme;

const WrapperBox = ({ children }) => (
  <Box
    elevation={"large"}
    width={"500px"}
    round="8px"
    background={colors.white}
    pad={"large"}
    gap={"medium"}
  >
    {children}
  </Box>
);

const EmailBlock = () => {
  const [isLoading, setLoading] = useState(true);

  useEffect(() => {
    setTimeout(() => {
      setLoading(false);
    }, 2000);
  });

  return (
    <>
      {isLoading && (
        <WrapperBox>
          <LoadingBlock />
        </WrapperBox>
      )}
      {!isLoading && (
        <WrapperBox>
          <Heading level={1} color={colors.black}>
            Become smarter in just 5 minutes
          </Heading>
          <Text size={"medium"}>
            Get the daily email that makes reading the news actually enjoyable.
            Stay informed and entertained, for free.
          </Text>
          <TextField
            id="outlined-basic"
            type={"email"}
            label={"Enter your email"}
            placeholder={"Enter your email"}
            variant="outlined"
          />
          <Button
            type="submit"
            onClick={(e) => {
              e.preventDefault();
            }}
            // disabled={isProcessing || !isValid}
            background={colors.primary}
            color={colors.white}
            style={{
              paddingTop: "16px",
              paddingBottom: "16px"
            }}
          >
            Submit
          </Button>
        </WrapperBox>
      )}
    </>
  );
};

export default EmailBlock;

Ve výše uvedeném kódu jsme vytvořili obalový box, který nám poskytne stejný přesný vzhled a chování bez ohledu na to, zda zobrazujeme data nebo ve stavu načtení. K simulaci stavu načítání také používáme React useState isLoading. Výchozí nastavení je true. Přidáváme také useEffect, který nám pomůže odstranit stav načítání po dvou sekundách.

...
useEffect(() => {
    setTimeout(() => {
      setLoading(false);
    }, 2000);
  });
...

Po dokončení by se vaše komponenta měla spustit ve stavu načítání a po dvou sekundách by se měla vymazat.

Přidání komponenty skeleton loader

Vraťte se na LoadingBlock.js soubor a přidejte následující:

import React from "react";
import Skeleton from "@material-ui/lab/Skeleton";
import { makeStyles } from "@material-ui/core";
import { Box } from "grommet";

const useStyle = makeStyles({
  box: {
    borderRadius: "6px"
  }
});

const LoadingBlock = () => {
  const classes = useStyle();
  return (
    <Box gap={"medium"}>
      <Skeleton
        variant="rect"
        width={"100%"}
        height={80}
        animation={"wave"}
        className={classes.box}
      />
      <Box gap={"xsmall"}>
        {[...new Array(5)].map(() => (
          <Skeleton
            variant="rect"
            width={"100%"}
            height={10}
            animation={"wave"}
            className={classes.box}
          />
        ))}
      </Box>
      <Skeleton
        variant="rect"
        width={"100%"}
        height={50}
        animation={"wave"}
        className={classes.box}
      />
      <Skeleton
        variant="rect"
        width={"100%"}
        height={50}
        animation={"wave"}
        className={classes.box}
      />
    </Box>
  );
};

export default LoadingBlock;

Závěr

A je to! V této části série nastavíte čistou a organizovanou komponentu React a navrstvíte ji do krásného nakladače kostry.

Přejděte na část 2 tohoto tutoriálu, kde budeme implementovat Zustand!

Dokončení aplikace až do tohoto okamžiku najdete na této karanténě kódu.