React has exploded in popularity — and for good reason! It makes building real-world apps a breeze.
The thing is, React comes with a boatload of buzzwords. Next.js and GraphQL and Gatsby and Redux and CSS-in-JS and how am I supposed to get anything done when keeping up with the ecosystem is a full time job in itself?
Forget the buzzwords.
It’s easy to grow tired of learning new tools every other week, so here’s a little secret: you can accomplish amazing things with nothing but the fundamentals.
#React first, ecosystem later
It’s true that React’s ecosystem is invaluable. In the right circumstances, tools like Redux, Styled Components and Next.js can give you a serious leg up. And what’s more, there’s a huge amount of material out there for full stack React.
But say that you’re just starting out.
Trying to learn React and its ecosystem at the same time can be confusing at the best of times. Not to mention that anything you learn about the ecosystem can quickly go out of date — while fundamental React concepts like elements, components and JSX have a well-deserved reputation for stability.
Here’s the thing: learning React by itself will give you the most solid foundation available. But what’s more, it’ll also let you get a darn lot done right now. That’s because with modern React, you don’t just get a library, you also get a configuration-free build system called Create React App — which we’ll be using in this course. And speaking of builds…
#What you’ll build
This course is designed to make you learn by doing. It includes a ton of live exercises, which you can complete comfortably within the browser. And while there are too many exercises to list them all here, there are two recurring projects that you’ll be gradually piecing together.
#The fractal tree
We’ll start by exploring React’s basics with this fractal tree. The code is live — try playing around with the numbers next to lean
and sprout
to change the animation, or try moving your mouse over the tree to see it respond!
import React, { useEffect, useState } from 'react'
import ReactDOM from 'react-dom'
import FractalTreeBranch from './FractalTreeBranch'
// The height and width of the entire window
const { innerHeight, innerWidth } = window
const FractalTree = () => {
let [time, setTime] = useState(() => Date.now())
let [mousePosition, setMousePosition] = useState({
x: innerWidth / 2,
y: innerHeight / 2,
})
useEffect(() => {
let requestId = window.requestAnimationFrame(() => {
setTime(Date.now())
})
return () => {
window.cancelAnimationFrame(requestId)
}
})
let fromHorizontalCenter = (innerWidth / 2 - mousePosition.x) / innerWidth
let fromVerticalCenter = (innerHeight / 2 - mousePosition.y) / innerHeight
let lean = 0.03 * Math.sin(time / 2000) + fromHorizontalCenter / 4
let sprout =
0.3 +
0.05 * Math.sin(time / 1300) +
fromVerticalCenter / 5 -
0.2 * Math.abs(0.5 - fromHorizontalCenter / 2)
return (
<div
style={{
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
overflow: 'hidden',
}}
onMouseMove={event => {
setMousePosition({
x: event.clientX,
y: event.clientY,
})
}}>
<FractalTreeBranch lean={lean} size={150} sprout={sprout} />
</div>
)
}
ReactDOM.render(<FractalTree />, document.getElementById('root'))
While fractal trees are fun, billionaires are better at paying the bills. That’s not actually why we’ll be building this contact list though — it’s more because it introduces useful things like forms, APIs and structure. Also, seeing your name in a list of billionaires is a lot of fun. Just try it by adding your name below!
import React from 'react'
import ReactDOM from 'react-dom'
import { createRecord, deleteRecord, getRecords, patchRecord } from './api'
import { Contact, ContactForm, ContactList } from './Contacts'
class App extends React.Component {
constructor() {
super()
this.state = {
contacts: [],
contactsError: null,
contactForm: {
name: '',
email: '',
},
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 = (
<p>
{this.state.contactsError}
</p>
)
}
else {
content = this.state.contacts.map((contact, i) =>
this.state.contactFormId === contact.id ? (
<ContactForm
key={i}
value={this.state.contactForm}
errors={this.state.contactFormErrors}
onChange={this.changeContactForm}
onClickCancel={this.cancelEditingContact}
onSubmit={this.patchContact}
/>
) : (
<Contact
{...contact}
key={i}
error={
this.state.deleteContactError === contact.id &&
"Could not be deleted."
}
onClickEdit={this.startEditingContact}
onClickDelete={this.deleteContact}
/>
)
)
}
return (
<ContactList onClickRefresh={this.refresh}>
{content}
{!this.state.contactFormId && (
<ContactForm
errors={this.state.contactFormErrors}
value={this.state.contactForm}
onChange={this.changeContactForm}
onSubmit={this.addContact}
/>
)}
</ContactList>
)
}
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: {
name: '',
email: '',
},
contactFormErrors: null,
})
},
() => {
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: {
name: '',
email: '',
},
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(
<App />,
document.getElementById('root')
)
#What will you learn?
You’ll learn everything you need to build kick-ass apps.
- Props
- Elements
- Components
- State
- Events
- Class components and lifecycle methods
- Forms and validation
- Working with apis
- Structure
- Basic hooks
- Effects
Context coming soon
Along the way, you’ll also get detailed discussion on pain points including:
- 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
?
#Are you ready to level up your Frontend game?
Okay, so say you want complete access to this course. How do you get it?
You’ve already got access to it! It’s $40/month $18/month absolutely free!
This course was originally part of Frontend Armory Pro, but due to the demonetization of this website, you can now access it for free — no strings attached.
So are you ready to level up your frontend game? Then what are you waiting for‽ Click the button below to dive into lesson one right now!
Start the first lesson – it's free!