Data Management

Nov 2021

DRY vs AHA!

DRY

Don’t Repeat Yourself

AHA!

Avoid Hasty Abstractions

all about keeping the balance

Page layouts

Composition

const MyWrapperComponent = ({ children, ...otherProps }) => (
  <>
    {children}
    <span>{otherProps?.title}</span>
  </>
)

Layout components

const Layout = ({ children, seoData }) => (
  <>
    <SEOcomponent data={seoData} />
    <HeaderComponent />
    <main>
      {children}
    </main>
    <FooterComponent />
  </>
)

Share data

const Layout = ({ children, seoData, ...otherProps }) => (
  <>
    <SEOcomponent data={seoData} />
    <HeaderComponent {...otherProps.propsForHeader}/>
    <main>
      {React.cloneElement(props.children, otherProps.moreProps)}
    </main>
    <FooterComponent {...otherProps.propsForFooter}/>
  </>
)

...keep going

const Comp1 = (layoutProps) => (
  <>
    <Comp2 {...layoutProps}/>
    <Comp3 />
  </>
)

const Comp2 = ({ id, foo, bar }) => (
  <>
    <h2>{id}</h2>
    <p className={foo}>{bar}</p>
  </>
)

Prop Drilling

when props are passed from Component to another by going through others which don’t need those props

React Context

Layout with Context

const Layout = ({ children, seoData, ...otherProps }) => (
  <DataProvider value={otherProps}>
    <SEOcomponent data={seoData} />
    <HeaderComponent />
    <main>
      {children}
    </main>
    <FooterComponent />
  </DataProvider>
)

No more drilling

const Comp1 = (layoutProps) => (
  <>
    <Comp2 {...layoutProps}/>
    <Comp3 />
  </>
)

const Comp2 = ({ id, foo, bar }) => (
  <>
    <h2>{id}</h2>
    <p className={foo}>{bar}</p>
  </>
)

Layout patterns

data

const Layout = ({ children, seoData, ...otherProps }) => (
  <DataProvider value={otherProps}>
    <SEOcomponent data={seoData} />
    <HeaderComponent />
    <main>
      {children}
    </main>
    <FooterComponent />
  </DataProvider>
)

ui

Separation of Concerns

const DefaultLayout = ({ children, seoData }) => (
  <>
    <SEOcomponent data={seoData} />
    <HeaderComponent />
    <main>
      {children}
    </main>
    <FooterComponent />
  </>
)

ui-only

Default layout

const DataLayout = (data, otherProps) => (
  <DataProvider value={data}>
    <DefaultLayout {...otherProps} />
  </DataProvider>
)

data-only

Data layout

improving DX

fallback Layout

getLayout

import { DefaultLayout } from '@layouts/default'

function MyApp({ Component, pageProps }) {

  const getLayout =
    Component.getLayout ||
    (page => <DefaultLayout>{page}</DefaultLayout>)

  return getLayout(<Component {...pageProps} />)
}

Page component

import { AuthLayout } from '@layouts/auth'

// ...

Page.getLayout = function AthenticatedAuth(page) {
  return <AuthLayout>{page}</AuthLayout>
}

Context Hell

export const DefaultLayout = ({ children }) => {
  return (
    <AuthProvider>
      <UserProvider>
        <ThemeProvider>
          <SpecialProvider>
            <JustAnotherProvider>
              <VerySpecificProvider>
                {children}
              </VerySpecificProvider>
            </JustAnotherProvider>
          </SpecialProvider>
        </ThemeProvider>
      </UserProvider>
    </AuthProvider>
  )
}

Jotai: create Atoms

import { atom, useAtom } from 'jotai'

export const authAtom = atom('value')

Jotai: setup Atoms

import {
  AuthAtom,
  UserAtom,
  ThemeAtom,
  SpecialAtom,
  JustAnotherAtom,
  VerySpecificAtom
} from '@atoms'
 
export const DEFAULT_VALUES = [
  [AuthAtom, 'value1'],
  [UserAtom, 'value2'],
  [ThemeAtom, 'value3'],
  [SpecialAtom, 'value4'],
  [JustAnotherAtom, 'value5'],
  [VerySpecificAtom, 'value6']
]

Jotai: usage

import { Provider } from 'jotai'
import { DEFAULT_VALUES } from '@utils/states'

export const DefaultLayout = ({ children }) => {
  return (
    <Provider initialValues={DEFAULT_VALUES}>
      {children}
    </Provider>
  )
}

Jotai + Next.js

SSR serialize all rendered props

Jotai passes down Promises

Promises can’t be serialized

/pages

conventions

slug.tsx

/api/slug.ts

_document.tsx

runs on every route, only on the server

creates any route

creates an RESTful endpoint

initializes every page

_app.tsx

serverless function

Serverless functions

import type { NextApiRequest, NextApiResponse } from 'next'

export default function handler (req: NextApiRequest, res: NextApiResponse) {
  res.status(200).json({ name: 'John Doe' })
}

Serverless functions

export default function handler (req, res) {
  res.setHeader('Access-Control-Allow-Origin', '*')
  res.setHeader(
    'Cache-Control',
    `private, s-maxage=${DAY_IN_SECONDS}, must-revalidate`
  )


  res.status(200).json({ name: 'John Doe' })
}

/pages/api

- REST api out-of-the-box

- Authomatic TS compilation

- no CORS by default

- dynamic routes supported

Dynamic routes

[...slug].ts

[[...slug]].ts

endpoint.ts

https://yourdomain.com/api/endpoint

https://yourdomain.com/api/endpoint-1

https://yourdomain.com/api/endpoint-X

https://yourdomain.com/api/endpoint-1

https://yourdomain.com/api/endpoint-X

https://yourdomain.com/api/

authentication

OAuth 2.0

OAuth is a standard apps use to provide apps with “secure delegated access”.

OAuth authorizes devices, APIs, servers, and applications with access tokens rather than credentials

JSON Web Token

each request carries a JWT, allowing the user to access routes, services, and/or resources 

client sends  JWT in the Authorization header using the Bearer schema

Authorization: Bearer <token>

Black Belt [Day 4]

By Atila

Black Belt [Day 4]

  • 386