Advanced React

A thorough overview of GraphQL, React, and React Testing

03 - An Intro to Next.js, Tooling, and Routing

Basics & Tooling

Next.js makes things simpler by handling all of the tooling for you such as React compiling, code splitting, etc under the hood for you. You can expose the Webpack and Babel config and provide your own.

Next.js also handles server-side rendering which is great for instant loading, SEO, and preloading. Provides a new React life cycle method called getInitialProps().

Routing

It also does routing for you. There really isn't much in the way of routing since you just create pages. Every single page that you want to visit must have a page.js file (very much like old PHP apps).

Next.js will take care of importing React into stateless functional components.

Next.js pages resolve to the file name less .js. So sell.js would resolve to http://my.app/sell. Everything that is resuable goes into components/ whereas the pages themselves go into the pages directory. Majority of logic and heavy duty lifting should go in the components directory. Like the pages directory to controllers which should be thin by their nature.

Links

To create a link bound to HTML pushstate (important so that you don't lose next.js cached data on the frontend) import Link from next/link as shown here in a stateless functional component:

import Link from 'next/link';

const Home = props => (
  <div>
    <p>Hey!!!</p>
    <Link href="/sell">
      <a>Sell!</a>
    </Link>
  </div>
);

export default Home;

The above link will take you to the /sell page.

04 - Custom _app.js Layout

Parent pages can handle state. Next.js by default wraps your entire application in an app component. You often want to create a custom wrapper for your application. It's good for persisting state between page changes, keeping state when navigating pages, custom error handling using `componentDidCatch, and inject additional data into pages (such as processing GraphQL queries).

The name of the custom app component is pages/_app.js. A simple custom app component would look like the following:

import App, {Container} from 'next/app';

class MyApp extends App {
  render() {
    const { Component } = this.props;

    return (
      <Container>
        <p>I'm on every page</p>
        <Component />
      </Container>
    );
  }
}

export default MyApp;

Use {this.props.children} inside of a render method to render out the children passed to the component.

To wrap every page's content in another component, you can create a generic page component and use that to wrap the content:

render() {
    const { Component } = this.props;

    return (
      <Container>
        <Page>
          <Component />
        </Page>
      </Container>
    );
}

Using lots of Stateless Functional Components since a lot of the state and life cycle methods will be handled by higher-order components and most of these are just display components.

05 - An Intro to Styled Components

CSS in JS is using the CSS inside of the JS for a specific button etc. You will still have global styles. Styled Components is one of the packages that you can use to help out.

When using styled components, you basically replace whatever element that you want to style with a component that is already styled.

Tag template literals can be used to create the styled components. They are basically just strings that have been tagged with a specific identifier.

import styled from 'styled-components';
const MyButton = styled.button`
	background: red;
	font-size: 100px;
`;

The above basically just creates a button element with the styles listed in the backticks. This allows you to reuse the styles for multiples of the same element. These styled elements are converted to React components. You can also target inner elments.

Don't go overboard creating individual styled components unless it makes sense to reuse the styled components. Otherwise, it's ok to use nested selectors.

06 - Themes and Layout with Styled Components

How to organize your styled components:

  1. you can put the styled components in the same component in which you're going to use them. That works well when the only thing you're going to be using that styled component for is in that file.

  2. Another format is to create a styles directory and put all of your styled components in that directory.

  3. Make it contextual. i.e. have a Header folder with a styles.js and index.js.

React Context API: it allows you to specify values up high and any child can access those values without having to pass down values from component to component and its via context.

07 - Global Styling and Typography with Styled Components

One of the benefits of css in js is that it scopes things. The alternative argument is that CSS is cascading. It's good to use the cascading nature through global styles.

Import packages first and local components second. Styled components by default don't render on the server.

08 - Visualizing Route Changes

In production, next.js prebuilds pages. In development, it builds them on demand. You can also prefetch the data.

Next.js has route lifecycle events. Use next/router and NProgress for routing. NProgress is for showing routing transition updates.

09 - Fixing Styled Components Flicker on Server Render

Flash of unstyled content. With server side rendering, everything needs to be prepped and ready to go before it is sent to the browser. Otherwise, the css might arrive late. With Next JS, you need to use a custom document (and while using styled components) so that it renders on the server side. collectStyles trawls your components to compile your css files before the render.

10 - An Intro To GraphQL

GraphQL is language agnostic. GraphQL is a single endpoint that you hit. RPC-esque. Self-documenting. GraphQL is a typed language. Requests are nested structures. As long as you have relations between your data you can pull back related structures as far as you want.

You don't have filters etc by default with GraphQL. Your server implements resolvers where you interact with MySQL etc.

11 - Getting Setup with Prisma

Prisma is an abstraction layer that sits on top of multiple databases to allow for easy interaction with GraphQL.

npm -i -g prisma, prisma login, prisma init.

When defining the graphql schema, use an ! to define if the field is required: email: String!. Or an array: email: [String!]!. Directives in GraphQL can do pretty much anything you want. @unique, @relation, @default.

A simple insert would look like:

mutation {
  createUser(data: {
    name: "Wes Bos"
    email: "hey@cool.com"
  }) {
    name
    email
  }
}

Where query:

query {
  users(where: {
    name_contains: "wes"
  }) {
    id
    name
  }
}

12 - Getting our GraphQL Yoga Server Running

When you need to expose an "endpoint" or the ability to run a query in GraphQL, you either need to write a resolver or a mutator. Resolvers are for retrieving data and mutators are for changing data.

13 - Our first query and mutation

You must define types in GraphQL. Required types are marked by !:

type Dog {
    name: String!
}

type Query {
    dogs: [Dog!]!
}

[Dog!]! means that the array is required and all elements inside the array must not be null.

Whenever you create a query, you must create a resolver which answers the question of how to get the data to the end user.

Inside of your resolvers and mutators files, you must match the queries and mutations defined in the schema.graphql file. For example, dogs above would reference dogs(parent, args, context, info) in the Query.js module. The context parameter refers to the context in createServer() which includes access to the db and information about the request.

A query for dogs would look like:

query {
  dogs {
    name
  }
}

That would require an array of dogs with only the name parameter added in:

{
  "data": {
    "dogs": [
      {
        "name": "Snickers"
      },
      {
        "name": "Sunny"
      }
    ]
  }
}

Resolvers and mutators can retrieve or send their data from or to anywhere. It can be a API, a flat file, or any other data source.

If you have something defined in your resolvers that isn't in your schema then it will error out.

Mutations are commonly written like the following:

mutation {
  createDog(name: "snickers") {
    name
  }
}

You can name queries and mutations:

mutation myReallyCompellingName {
  createDog(name: "snickers") {
    name
  }
}

14 - Items Creation and Prisma Yoga Flow

The steps for adding a new piece of data to an API are:

  • data model and deploy
  • edit schema
  • write resolver

When you have more parameters to a mutator than what is reasonable, you can migrate it to a single parameter called data which would be a new data type.

type Mutation {
    createItem(title: String, description: String, price: Int, image: String, largeImage: String): Item!
}

# to 

type Mutation {
    createItem(data: ItemCreateInput!): Item!
}

15 - Setting Up Apollo Client with React

With Apollo you don't need to use Redux since it does all of the data management stuff that Redux does. withData higher order Apollo component exposes Apollo as a prop. To add Apollo, just wrap the application in an Apollo provider. Wrap your app with the withData to add in Apollo.

getInitialProps is a special NextJS lifecycle method that's added to React by NextJS.

Context needs to be defined as ctx in order for the import to work.

16 - React Meets GraphQL

Recommended by the Apollo team to locate the query in the file that you are going to be using it in. If it will be used by multiple files, use it in one of the files and then export it to the rest.

It's a best practice to make queries variable names all caps and name queries after the variable names:

const ALL_ITEMS_QUERY = gql`
  query ALL_ITEMS_QUERY {
      
  }
`;

Wrap your component with a high-order component (Apollo has one) to expose a list of props. However, render props are becoming more popular. You don't get the render and loading states when using high-order components.

Render props look like the following (they must be passed a function as the children):

export default class Items extends Component {
  render() {
    return (
      <div>
        <p>Items!</p>
        <Query query={ALL_ITEMS_QUERY}>
          {({data, error, loading}) => {
            if (loading) return <p>Loading...</p>;
            if (error) return <p>Error: {error.message}</p>;
            return <p>Found {data.items.length} items</p>;
          }}
        </Query>
      </div>
    )
  }
}

REMEMBER: keys in react MUST be unique.

In react, when passing a value to a prop, the first set of curly's refers to an object reference wheres a second would refer to an object literal:

<Link href={{
  pathname: '/item',
  query: { id: item.id }
}}>
  <a>{item.title}</a>
</Link>

17 - Creating Items with CRUD

Use htmlFor in React instead of for on a label tag since for is a reserved word in Javascript.

If you try to retrieve values directly from an input, it won't work since the values are then being managed in two different places and React won't allow that. The input thus become read only. You need to add an onChange handler.

On change handlers need to be instance properties so that they can access this. ES6 classes do not bind regular methods to the instance. Event handlers receive an event object.

Use this.setState({}) to set the state of a value. You can use computed property names like so: this.setState({ [name]: val }).

Normally in html, textarea cannot have a value prop nor can they be self-closing. But in React they can.

Use the Mutation component to use a mutation:

<Mutation query={CREATE_ITEM_MUTATION} variables={this.state}></Mutation>

Use aria-busy to let users know if the fieldset is busy.

19 - Updating Items with Queries and Mutations