React (without the buzzwords)

No Webpack, no Redux, no GraphQL.

Behind all the buzzwords, React is just JavaScript.

In this course, you'll master React's fundamentals without touching Redux, Webpack, or GraphQL. And along the way you'll build a real-world app with form validation, asynchronous storage, and proper structure.

Try the first lesson
   Unlock access

Companies everywhere are moving to React, and for good reason. It makes building real-world apps a breeze!

But React comes with a boatload of buzzwords. Redux and GraphQL and Webpack and MobX and how am I supposed to get anything done when keeping up with the ecosystem is a full time job in itself?

#What you’ll learn


#Forget the buzzwords

A few years ago, I’d grown tired of learning new tools every other week. I wanted to make something that’d last, so I created a short guide called Learn Raw React. It teaches you the fundamentals, and completely skips anything that could change - there’s no webpack, no redux, and even no JSX!

Now you might think that you can’t do much with just Raw React. But surprisingly, developers loved it! They found that focusing on Raw React revealed what’s really going on, and it helped them feel more confident with real world code to boot. Even one of Redux’s co-authors chimed in:

But while developers found themselves more confident after learning about elements and reconciliation, React was still in it’s infancy. Many patterns had yet to be discovered. And many APIs were still just sparks in the React team’s imagination.

#Raw React is becoming more powerful… and more important.

Fast forward to 2018. React’s API is growing again. Planned features like Time Slicing and Suspense will let you do more with React itself. More people are replacing state management libraries with plain component state… including people within Facebook itself.

New React APIs mean less need for 3rd party tools. More than ever, your career depends on a deep understanding of React itself.

But despite the growing importance of the fundamentals, many still struggle with the same questions:

  • What’s the difference between elements, components and instances?
  • How does JSX actually work?
  • Why should I use key props with arrays?
  • When is it ok to use this.state?

I’ve seen how valuable Raw React can be as a tool to build confidence with the fundamentals. But there’s only so much you can put in a blog post…

#Learn the fundamentals without leaving your browser

I believe that to truly master the fundamentals, you need actual experience working with them. I don’t want to waste your time on feel-good bullshit lessons that you just use to pacify your boss. I want to help you truly master React’s fundamentals.

This course is composed of 30 lessons, with 55 live examples and exercises. It lets you save your progress through lessons, and automatically saves your exercise answers so you can pick up where you left off. The course is designed to encourage you to learn by doing — the only way to truly learn something back to front.

By working through the course, you’ll also gain experience with real-world code - you won’t be stuck building counter components. By the end of the course, you’ll have built this full contact list with form validation, asynchronous storage, and proper structure.

main.js
Contacts.js
api.js
styles.css
index.html
import { createRecord, deleteRecord, getRecords, patchRecord } from './api.js'
import { Contact, ContactForm, ContactList } from './Contacts.js'
const { createElement } = React

class App extends React.Component {
  constructor() {
    super()
    
    this.state = {
      contacts: [],
      contactsError: null,
      
      contactForm: {},
      contactFormErrors: null,
      contactFormId: null,
    }
    
    this.addContact = this.addContact.bind(this)
    this.cancelEditingContact = this.cancelEditingContact.bind(this)
    this.changeContactForm = this.changeContactForm.bind(this)
    this.deleteContact = this.deleteContact.bind(this)
    this.patchContact = this.patchContact.bind(this)
    this.refresh = this.refresh.bind(this)
    this.startEditingContact = this.startEditingContact.bind(this)
  }

  componentDidMount() {
    this.refresh()
  }

  render() {
    let content
    if (this.state.contactsError) {
      content = createElement(
        'p',
        {},
        this.state.contactsError,
      )
    }
    else {
      content = this.state.contacts.map((contact, i) =>
        this.state.contactFormId === contact.id ? (
          createElement(
            ContactForm,
            {
              key: i,
              value: this.state.contactForm,
              errors: this.state.contactFormErrors,
              onChange: this.changeContactForm,
              onClickCancel: this.cancelEditingContact,
              onSubmit: this.patchContact,
            }
          )
        ) : (
          createElement(
            Contact,
            {
              ...contact,
              key: i,
              error:
                this.state.deleteContactError === contact.id &&
                "Could not be deleted.",
              onClickEdit: this.startEditingContact,
              onClickDelete: this.deleteContact,
            }
          )
        )
      )
    }
  
    return createElement(
      ContactList,
      {
        onClickRefresh: this.refresh,
      },
      content,
      !this.state.contactFormId && createElement(ContactForm, {
        errors: this.state.contactFormErrors,
        value: this.state.contactForm,
        onChange: this.changeContactForm,
        onSubmit: this.addContact,
      }),
    )
  }
  
  changeContactForm(value) {
    this.setState({
      contactForm: value,
      contactFormErrors:
        silenceRectifiedErrors(this.state.contactFormErrors, validateContact(value))
    })
  }
  
  addContact() {
    let errors = validateContact(this.state.contactForm)
    
    if (errors) {
      this.setState({
        contactFormErrors: errors,
      })
    }
    else {
      createRecord(this.state.contactForm).then(
        (response) => {
          this.setState({
            contacts: this.state.contacts.concat(response.data),
            contactForm: {},
            contactFormErrors: null,
          })
        },
        (error) => {
          this.setState({
            contactFormErrors: {
              base: "Something went wrong while saving your contact :-(",
            }
          })
        }
      )
    }
  }
  
  patchContact() {
    let errors = validateContact(this.state.contactForm)
    
    if (errors) {
      this.setState({
        editingErrors: errors,
      })
    }
    else {
      let id = this.state.contactFormId
    
      patchRecord(id, this.state.contactForm).then(
        (response) => {
          // Make a clone of the stored contacts.
          let newContacts = this.state.contacts.slice(0)
          
          // Find the contact with the correct id
          let i = newContacts.findIndex(contact => contact.id === id)
          
          // Update the contact in this.state
          newContacts[i] = response.data
        
          this.setState({
            contacts: newContacts,
            contactForm: {},
            contactFormId: null,
            contactFormErrors: null,
          })
        },
        (error) => {
          this.setState({
            contactFormErrors: {
              base: "Something went wrong while saving your contact :-(",
            }
          })
        }
      )
    }
  }
  
  deleteContact(id) {
    deleteRecord(id).then(
      (response) => {
        this.setState({
          contacts: this.state.contacts.filter(contact => contact.id !== id),
          deleteContactError: null,
        })
      },
      (error) => {
        this.setState({
          deleteContactError: id,
        })
      }
    )
  }
  
  cancelEditingContact() {
    this.setState({
      contactForm: {},
      contactFormId: null,
      contactFormErrors: null,
    })
  }
  
  startEditingContact(id) {
    this.setState({
      contactFormId: id,
      contactForm: this.state.contacts.find(contact => contact.id === id),
    })
  }

  refresh() {
    getRecords().then(
      (response) => {
        this.setState({
          contacts: response.data,
          contactsError: null,
        })
      },
      (error) => {
        this.setState({
          contactsError: "Your contacts couldn't be loaded :-("
        })
      }
    ) 
  }
}

function validateContact(contact) {
  let errors = {}
  if (!contact.name) {
    errors.name = "You must enter a name."
  }
  if (!contact.email) {
    errors.email = "You must enter an email."
  }
  else if (!isEmailValid(contact.email)) {
    errors.email = "That doesn't look like a valid e-mail."
  }
  if (Object.keys(errors).length) {
    return errors
  }
}

function isEmailValid(value) {
  return value.indexOf('@') !== -1
}

function silenceRectifiedErrors(oldErrors, newErrors) {
  if (newErrors && oldErrors) {
    let errors = {}
    for (let key of Object.keys(newErrors)) {
      if (oldErrors[key]) {
        errors[key] = oldErrors[key]
      }
    }
    return Object.keys(errors).length ? errors : null
  }
}

ReactDOM.render(
  React.createElement(App),
  document.getElementById('root')
)
Build In Progress

#Like a book, just better

React (without the buzzwords’) lessons are text and exercise based. There’s not a video in sight.

This has a number of benefits. It’s easier to re-read a paragraph than to rewind a video. It’s easier to skim ahead. And it’s easier to look back and forth between the text and the exercises. This is crucial, because the exercises are how you really learn the fundamentals.

#It’s an investment

There are many patterns and APIs that you’ll need to understand to advance through your React career. Frontend Armory can help you learn these! But creating effective courses takes time. This course took months to create.

On the flip side, investments have returns. You will become a better React developer by learning the fundamentals. And you will be able to command a higher salary if you’re a React pro.

React (without the buzzwords) is exclusive to Frontend Armory Pro members. Of course, there’s more benefits to Frontend Armory Pro than just this course. And if they’re not worth far more than the price of the monthly membership, then please send me an e-mail and let me know why! But otherwise…

Are you ready to invest in a solid foundation for your React career?

#Learn React without the buzzwords

Start by trying out the free preview lessons - just click a link below. There’s no signup required, you can get started immediately!

Want to know if the course matches your level? Not sure if the format suits you? Or maybe you just want to have a look first? Then try out the free lessons - you’ve got nothing to lose!

Or if you’re ready to get started? Great!



And before you go, I just wanted to say thanks to you for reading this! I couldn’t keep going without the support of readers like yourself. So thank you!