Warren Shea

Warren Shea’s Notes for Advanced React (Online Course)

https://courses.wesbos.com/ | https://advancedreact.com/ | https://github.com/wesbos/Advanced-React
Version: 20220729 | Status: Paused at Module 7 - will continue as needed


Table of Contents

Module 01: Introduction and Setup

Module 02: Learning Next.js

Module 03: CSS and Styled Components

Module 04: Server Side GraphQL Development

Module 05: Client Side React + GraphQL Development

Module 06: Working with Mutations

Module 07: Pagination

Module 08: User Registration + Authentication

Module 09: Shopping Cart Development

Module 11: Order Creation and Checkout

Module 12: Frontend Order Displaying

Module 13: Roles, Permissions and Restricting Access

Module 14: Testing


Module 01.01 Tooling and Starter Files Setup

Module 01.02 The Tech Stack Explained

Module 02.01 An intro to Next

Module 02.02 Creating a Page Layout Component

Page.propTypes = { children: PropTypes.any, cool: PropTypes.string, }

import Page from ‘../components/Page’;

export default function IndexPage() { return ( <p>1</p> <p>2</p> ) }


```html

<div>
  test
  <p>1</p>
  <p>2</p>
</div>

export default class MyDocument extends Document { render() { return ( <Html lang="en-CA"> <Head></Head> <body> <Main /> </body> </Html> ) } }


## Module 02.03 Creating our Header and Nav Components
* Creating a header/nav component
* NextJS uses HTML push.state instead of anchor, you so can use <Link>, `import Link from 'next/link'`

## Module 03.01 An Intro to Styled Components and CSS
* `import styled from 'styled-components'`
* Example of styled components

```jsx
const Logo = styled.h1`
  background: red;
  a { color: white; }
`;

Module 03.02 Global Styles, Typography and Layout Styles

export default function Page({children,cool}) { return ( <div> <Header /> {children} </div> ) }


## Module 03.03 Visualizing Route Changes
* Line across the top for progress (nprogress)
* Example for main `<Page>`
```jsx
import 'Nprogress' from nprogress;
import 'nprogress/nprogress.css'; //Can use your own CSS instead
import Router from 'next/router';

Router.events.on('routeChangeStart', () > NProgress.start());
Router.events.on('routeChangeComplete', () > NProgress.done());
Router.events.on('routeChangeError', () > NProgress.done());

Module 03.04 Fixing Styled Components Flicker on Server Render

export default class MyDocument extends Document { static getInitialProps({ renderPage }) { const sheet = new ServerStyleSheet(); const page = renderPage(App => props => sheet.collectStyles(<App {…props} />)); const styleTags = sheet.getStyleElement(); return { …page, styleTags }; } render() { return ( <Html lang="en-CA"> <Head></Head> <body> <Main /> </body> </Html> ) } }


## Module 04.01 Setting up MongoDB
* Keystone runs ontop of MongoDB/Postgres
* Mongo Atlas for cloud version
* Rename sample.env to .env

## Module 04.02 An Intro to GraphQL
* GraphQL - specification for requesting/pushing data to server
* Keystone, Apollo build a layer ontop of GraphQL
* In Keystone, can see API Explorer
*
```graphql
query {
  allProducts {
    name
    description
    price
  }
}

Module 04.03 Setting up Keystone and Typescript

* `npm run dev` to run keystone

## Module 04.04 Creating our first User data type
* Every time there's a datatype, we build a schema
* Set up Users section on Keystone

## Module 04.05 Adding Auth to our Application
* Adding Auth to Keystone
* Adding Session to Keystone

## Module 04.06 Creating our Products Data Type
* Creating Product list

## Module 04.07 Uploading Product Images
* Cloudinary - a service with a very generous free tier
* Can use NextJS image tag to display images
* Anytime you need environment variable, need `import 'dotenv/config';`

## Module 04.08 Creating two way data relationships in Keystone
* in the schema, you can do `ProductImage.ts`
`product: relationship({ ref: 'Product.photo' })` for a two way data relationship in Keystone. This is the Product datatype and the photo field
and in `Product.ts`
`photo: relationship({ ref: 'ProductImage.product' })`
*

## Module 04.09 Inserting Seed Data
* In `keystone.ts`, you can add Seed data but we only want to do it if there's an argument
```typescript
db: {
  adapter: 'mongoose',
  url: databaseURL,
  async onConnect(keystone) {
    if(process.argv.includes('--seed-data')) {
      await insertSeedData(keystone);
    }
  },
},

Module 05.01 Setting up Apollo Client

Module 05.02 Fetching Data with hooks and Displaying it in our Front End

OR

export { default } from ‘./products’;

* How to query items from backend
```graphql
query ALL_PRODUCTS_QUERY {
  allProducts {
    id
    name
    price
    description
    photo {
      id
      image {
        publicUrlTransformed
      }
    }
  }
}
import { useQuery } from '@apollo/client';
import gql from "graphql-tag";

const ALL_PRODUCTS_QUERY = gql`
  query ALL_PRODUCTS_QUERY {
    allProducts {
      id
      name
      price
      description
      photo {
        id
        image {
          publicUrlTransformed
        }
      }
    }
  }
`;

export default function Products() {
  const { data, error, loading } = useQuery(ALL_PRODUCTS_QUERY);
  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;
  return (
    <div>
      { data.allProducts.map(product => (
       <p key={products.id}>{product.name}</p>
      ))}
    </div>
  )
}

Module 05.03 Fixing and Styling the Nav

Module 05.04 A real good lesson in React Forms and Custom Hooks

export default function CreateProduct() { const [name, setName] = useState(‘Wes’); return ( <input value={name} onChange={(e) => { setName(e.target.value); }} > ) }

* useForm.js
```jsx
import { useEffect, useState } from 'react';

export default function useForm(initial = {}) {
  // create a state object for our inputs
  const [inputs, setInputs] = useState(initial);
  const initialValues = Object.values(initial).join(''); //* this can't be initial or it causes infinite loop

  useEffect(() => {
    // This function runs when the things we are watching change
    setInputs(initial);
  }, [initialValues]); //* this can't be initial or it causes infinite loop

  // {
  //   name: 'wes',
  //   description: 'nice shoes',
  //   price: 1000
  // }

  function handleChange(e) {
    let { value, name, type } = e.target;
    if (type === 'number') {
      value = parseInt(value);
    }
    if (type === 'file') {
      [value] = e.target.files; // value = e.target.files.value?
    }
    setInputs({
      // copy the existing state
      ...inputs,
      [name]: value,
    });
  }

  function resetForm() {
    setInputs(initial);
  }

  //blankState is turns object to array, changes values, then converts back to object
  function clearForm() {
    const blankState = Object.fromEntries( //turn to object
      Object.entries(inputs).map(([key, value]) => [key, '']) //turn to array and change values
    );
    setInputs(blankState);
  }

  // return the things we want to surface from this custom hook
  return {
    inputs,
    handleChange,
    resetForm,
    clearForm,
  };
}
import { useForm } from '../lib/useForm';

export default function CreateProduct() {
  const { inputs, handleChange } = useForm({
    name: 'blah', //for testing purposes
    price: 234234
  });
  return (
    <input
      name="name"
      value={inputs.name}
      onChange={handleChange}
    >
    <input
      name="price"
      value={inputs.price}
      onChange={handleChange}
    >
  )
}

Module 05.05 Hooking up our File input and Form Styles

Module 05.06 Creating Products via our Mutations

const [ createProduct, { loading, error, data }] = useMutation(CREATE_PRODUCT_MUTATION, { variables: inputs });

<form onSubmit={ async (e) => { e.preventDefault(); const res = await createProduct(); }}></form>

<DisplayError error={error} /> // done by Wes Bos <fieldset disabled={loading} aria-busy={loading}> //for loading


## Module 05.07 Refetching Queries after a Successful Mutation
* Go to a page, add product, go back - it's cached!
* Two options: modify cache directly in Apollo or tell Apollo behind the scenes to refetch
* Refect Query: Export Query (e.g. ALL_PRODUCTS_QUERY) and import it in new file,
```jsx
const [ createProduct, { loading, error, data }] = useMutation(CREATE_PRODUCT_MUTATION, {
  variables: inputs,
  refetchQueries: [{query: ALL_PRODUCTS_QUERY }] // refetchQueries: [{query: ALL_PRODUCTS_QUERY, variables }]
});

Module 05.08 Programmatically Changing the Page after product creation

<form onSubmit={ async (e) => { e.preventDefault(); const res = await createProduct(); clearForm(); Router.push({ pathname: /product/${res.data.createProduct.id}, }); } }></form>


## Module 05.09 Displaying Single Items, Routing and SEO
* Can have files like [id].js which Next will recognize
* You can only query single items (Product) based on unique fields (id)
```jsx
query {
  Product(where: {
    id: $id
  })
}

Module 06.01 Updating Items

Module 06.02 Using useEffect to deal with a tricky loading state issue

Module 06.03 Deleting Products

Module 06.04 Evicting Items from the Apollo Cache

Module 07.02 Pagination Dynamic Routing

Module 07.03 Adjusting our Query for Pagination Values

Module 07.04 Custom Type Policies and Control over the Apollo Cache

Module 08.01 Querying The Current User

Module 08.02 Creating a Sign In Component

Module 08.03 Creating a Sign Out Component

Module 08.04 Creating our Sign Up Flow

Module 08.05 Password Reset - Requesting a Reset

Module 08.06 Password Reset - Setting a new Password

Module 08.07 Password Reset - sending the email

Module 09.01 Cart - Creating the Data Type and Two Way Relationships

Module 09.02 Cart - Displaying Items in a Custom Component

Module 09.03 Cart - Using React Context for our Cart State

Module 09.04 Cart - Adding Items to Cart

Module 09.05 Cart - Adding Items To Cart in React

Module 09.06 Cart - Animating the Cart Cart Value

Module 09.07 Cart - Remove From Cart Button

Module 09.08 Cart - Evicting Cart Items from the Cache

Module 11.01 Setting Up our Stripe Checkout

Module 11.02 Writing our Client Side Checkout Handler Logic

Module 11.03 Creating our Order and OrderItem Data Types

Module 11.04 Custom Checkout Mutation with Stripe

Module 11.05 Linking up our Frontend to the custom backend checkout mutation

Module 11.06 Creating our Order and OrderItems in our Mutation

Module 11.07 Finishing up the Checkout UI and Flow

Module 12.01 Displaying a Single Order

Module 12.02 Displaying All Orders

Module 13.01 Roles and Permissions - A Primer

Module 13.02 Creating the Roles and Permissions Schema + UI

Module 13.03 Basic Access Control via Sessions

Module 13.04 Permissions Access Functions

Module 13.05 More Flexible Rule Based Functions

Module 13.06 Getting Meta - Roles based Roles and Hiding UI

Module 13.07 Cart and Order based Rules

Module 13.08 User and Field Based Permissions

Module 13.09 Product Image Permissions

Module 13.10 Creating a Gated Sign In Component

Module 14.01 Test Setup, Tooling and Methodology

Module 14.02 Testing Basics

Module 14.03 Testing our formatMoney function

Module 14.04 React Testing Library

Module 14.05 Snapshot Testing

Module 14.06 More on Testing Library Queries

Module 14.07 Mocking GraphQL Data Requests

Module 14.08 Updating Props and re-rendering

Module 14.09 Testing Signed in and Signed out Nav States

Module 14.10 Pagination Testing

Module 14.11 Testing User Events and Mutations

Module 14.12 Testing Password Reset Component

Module 14.13 Mocking 3rd Party Libraries