React Basics
09 May 2020Basic Hello World
// index.js
import React from "react"
import ReactDom from "react-dom"
// Using JSX
ReactDom.render(<h1>Hey</h1>, document.getElementById("root")
ReactDom.render
can only render one element. If you have sibling elements, you must wrap them in a parent element first.
Reusable Web Components
Sort of a fusion between Rails View partials and an Object.
// index.js
import React from "react"
import ReactDOM from "react-dom"
function MyApp() {
return(
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
)
}
ReactDOM.render(
<MyApp />,
document.getElementById("root")
)
Components can also only return one element. If you have sibling elements, you must wrap them in a parent element first.
Externalize Components
- Make a file with the exact name as your Component (
MyApp.js
for example). - In the external component import
react
. - Move your component from
index.js
into the file you just created. - Export component (
export default MyApp
). - Import component in
index.js
(import MyApp from "./MyApp"
), use relative path for component, & can leave out file extension.
Add some CSS
You need to use className
instead of class
because React uses the JavaScript DOM API, which has things like
document.getElementById("root").className
So you can style things like:
//...
return (
<header className="navbar">This is the header</header>
)
//...
You can’t add className
on JSX elements (the ones that look like HTML). You can’t add it to a Component
Interpolate JS in JSX
Just add the { }
function App() {
const firstName = "Jelani"
const lastName = "Woods"
return (
<h1>Hello {firstName + " " + lastName}!</h1>
)
}
Or in ES6
return (
<h1>Hello {`${firstName} ${lastName}`}!</h1>
)
Props
Conceptually similar to attribute/value pairs of an HTML element.
You give an <img>
element a src=""
to some value. That value can change and will dramatically differentiate one <img>
from another.
Relating more to components, props
are arguments to a component.
<ContactCard
name="Mr. Whiskerson"
imgUrl="http://placekitten.com/300/200"
phone="(212) 555-1234"
email="mr.whiskaz@catnap.meow"
/>
Use the props
in the Component.
function ContactCard(props) {
console.log(props)
return (
<div className="contact-card">
<img src={props.imgUrl}/>
<h3>{props.name}</h3>
<p>Phone: {props.phone}</p>
<p>Email: {props.email}</p>
</div>
)
}
State
— is data that Component maintains
props
are immutable. Components recieve them, but they cannot change them.
State can be changed.
- Component must be Class based.
- Add Constructor and call
super()
- Initialize
state
.
class App extends React.Component {
constructor() {
super()
this.state = {
answer: "Yes" // initial value
}
}
//...
Can pass state from Parent to Child Component through props.
render() {
return (
<div>
<h1>Is state important to know? {this.state.answer}</h1>
<ChildComponent answer={this.state.answer}/>
</div>
)
}
Changing the state with setState()
will automatically, update Child Components that received the state.
Event handling
Pretty much like regular JS. Camel-cased event HTML triggers, except you can write the function right there.
function App() {
return (
<div>
<img src="https://www.fillmurray.com/200/100"/>
<br />
<br />
<button onClick={() => console.log("I was clicked!")}>Click me</button>
</div>
)
}
setState
You can’t modify state
directly and probably don’t want to.
// BAD
handleClick() {
this.state.count++
}
Use setState
You can pass setState
an object that represents the new state.
this.setState({count: 1})
You can also pass a function to setState
.
this.setState(prevState => {
// ...
})
The function must return an Object that represents the new state
.
this.setState(prevState => {
return {
count: prevState.count + 1
}
})
This way you have a guarenteed way to accesss the previous state (this.state
is not guaranteed to be accurate).
TypeError: Cannot read property ‘setState’ of undefined
Annoyingly, if you’re using a function to call setState
you first need to bind this
to the current class.
constructor() {
super()
this.state = {
count: 0
}
this.handleClick = this.handleClick.bind(this)
}
// ...
// in render()
<button onClick={this.handleClick}>Change!</button>
Lifecycle Methods
-
render()
- not usually labelled as such. How the Component is displayed to the world. Can be called anytime thestate
orprops
change. -
componentDidMount()
- The very first time the Component shows up, React runs this method. Will only run once. Re-renders will not trigger this. Usually used for API call. -
componentWillReceiveProps()
- DEPRECATED: Run everytime Component receivesprops
. Also run when Parent Component sendsprops
to Child Component.componentWillReceiveProps(nextProps) { if (nextProps.whatever !== this.props.whatever) { // do something important here } }
-
shouldComponentUpdate(nextProps, nextState)
- returntrue
/false
if you want Component to re-render. -
componentWillUnmount()
- Used to teardown or clean up code before Component disappears.
static getDerivedStateFromProps(props, state) {
// return the new, updated state based upon the props
// https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html
}
getSnapshotBeforeUpdate() {
// create a backup of the current way things are
// https://reactjs.org/docs/react-component.html#getsnapshotbeforeupdate
}
componentDidUpdate()
-
componentDidUpdate() {
const newColor = randomcolor()
this.setState({color: newColor})
}
// Uncaught Invariant Violation: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
Since the render
is called anytime the state
is changed and componentDidUpdate
is called after render
, changing the state
will trigger a new render
which triggers a new componentDidUpdate
which updates the state
which triggers the render
… infinitely. Must only update state
based on condition.
componentDidUpdate(prevProps, prevState) {
if(prevState.count !== this.state.count) {
const newColor = randomcolor()
this.setState({color: newColor})
}
}
Conditional Rendering
Ternary
Of course, there are a LOT of different ways to do conditionals. Ternary operators, however, are pretty commonly used in React.
expression ? when true do this : otherwise do this
render() {
return (
<div>
{this.state.isLoading ?
<h1>Loading...</h1> :
<Conditional />}
</div>
)
}
Logical AND Operator
Usually, we think of &&
when writing some conditional and you only want the condition to be true
if both expressions are true
.
How JavaScript works under the hood in regards to &&
:
- the first expression on the left will be evaluated
- if the first expression is not true the whole condition is
false
- if the first expression is
true
, the second expression is immediately returned
This allows us to do some cleverness with conditional Component rendering.
Say we have a list of unread messages in state
and we only want to display them if the list has messages.
Using a Ternary we could have something like
{
this.state.unreadMessages.length > 0 ?
<h2>You have {this.state.unreadMessages.length} unread messages!</h2> :
null
}
But using the logical AND, we can do
render() {
return (
<div>
{
this.state.unreadMessages.length > 0 &&
<h2>You have {this.state.unreadMessages.length} unread messages!</h2>
}
</div>
)
}
Since if this.state.unreadMessages.length > 0
is true
, then <h2>You have {this.state.unreadMessages.length} unread messages!</h2>
will immediately be returned.
Can also do conditional styling
function TodoItem(props) {
const completedStyle = {
fontStyle: "italic",
color: "#cdcdcd",
textDecoration: "line-through"
}
return (
<div className="todo-item">
<input
type="checkbox"
checked={props.item.completed}
onChange={() => props.handleChange(props.item.id)}
/>
<p style={props.item.completed ? completedStyle: null}>{props.item.text}</p>
</div>
)
}
fetch
A Global vanilla JS function that makes HTTP calls. Docs
Promises
Reading from APIs
Often done in componentDidMount()
,
componentDidMount() {
fetch("https://swapi.co/api/people/1")
.then(response => response.json())
.then(data => console.log(data))
}
and of course we store the data
in state
somewhere.
Usually, it’s good practice to display some loading indicater if you’re reading from an API
componentDidMount() {
this.setState({loading: true})
fetch("https://swapi.co/api/people/1")
.then(response => response.json())
.then(data => {
this.setState({
loading: false,
character: data
})
})
}
Forms
Unlike other DOM elements in React, forms naturally keep some internal state.
Usually we have a JavaScript function that handles the submission of the form and has access to the data that the user entered into the form.
Instead of waiting for the User to submit the form before we validate and check the inputs, These Controlled Components will actually update the state after every keystroke.
- Make
state
the single source of truth.
import React, {Component} from "react"
class App extends Component {
constructor() {
super()
this.state = {
firstName: "",
lastName: ""
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(event) {
const {name, value} = event.target
this.setState({
[name]: value
})
}
render() {
return (
<form>
<input
type="text"
value={this.state.firstName}
name="firstName"
placeholder="First Name"
onChange={this.handleChange}
/>
<br />
<input
type="text"
value={this.state.lastName}
name="lastName"
placeholder="Last Name"
onChange={this.handleChange}
/>
<h1>{this.state.firstName} {this.state.lastName}</h1>
</form>
)
}
}
export default App
Different types of inputs
textarea
In React a <textarea />
is more similar to an <input>
, it’s a self-closing tag. We can use the value
property
checkbox
handleChange(event) {
const {name, value, type, checked} = event.target
type === "checkbox" ? this.setState({ [name]: checked }) : this.setState({ [name]: value })
}
<label>
<input
type="checkbox"
name="isFriendly"
checked={this.state.isFriendly}
onChange={this.handleChange}
/> Is friendly?
</label>
radio
<label>
<input
type="radio"
name="gender"
value="female"
checked={this.state.gender === "female"}
onChange={this.handleChange}
/> Female
</label>
select
<label>Favorite Color:</label>
<select
value={this.state.favColor}
onChange={this.handleChange}
name="favColor"
>
<option value="blue">Blue</option>
<option value="green">Green</option>
<option value="red">Red</option>
<option value="orange">Orange</option>
<option value="yellow">Yellow</option>
</select>
We can write a single handleChange
for every type of input in our forms.
Container and Presentation Components
Nicer separation of concerns!
Container Component
Job is to maintain state, methods that update state. Delegate presentation logic to other component.
Presentation Component
Job is to recieve props and displaying things correctly
Example,
Container Component delegates the state data to Presentation Component
// Container
render() {
<FormComponent
handleChange={this.handleChange}
data={this.state}
/>
}
// Presentation
<input
name="firstName"
value={props.data.firstName}
//...
Or with Object spread
render() {
<FormComponent
handleChange={this.handleChange}
{...this.state}
/>
}
Using Arrow Functions
Using regular function syntax, we need to bind this
.
class App extends Component {
constructor() {
super()
// ...
this.handleChange = this.handleChange.bind(this)
}
handleChange(event) {
this.setState({
// ..
})
}
But if we use the Arrow Function syntax, this
is taken from the surroundings, which in this context means we don’t need to bind it.
class App extends Component {
constructor() {
// ..
}
handleChange = (event) => {
const { name, value } = event.target
this.setState({
// ..
})
}
Other modern/advanced React features/topics to learn:
- Official React Context API - https://reactjs.org/docs/context.html
- Error Boundaries - https://reactjs.org/docs/error-boundaries.html
- render props - https://reactjs.org/docs/render-props.html
- Higher Order Components - https://reactjs.org/docs/higher-order-components.html
- React Router - https://reacttraining.com/react-router/core/guides/philosophy
- React Hooks - https://reactjs.org/docs/hooks-intro.html
- React lazy, memo, and Suspense - https://reactjs.org/blog/2018/10/23/react-v-16-6.html
Hooks
Hooks are a way to “hook into” state and lifecycle methods of Components without using classes.
= Allows us to only use functional Components
- Improve readability and organization of components
useState
import React, { useState } from "react"
function App() {
const value = useState()
console.log(value)
return (
<div>
<h1>Is state important to know? Yes</h1>
</div>
)
}
useState()
returns [ null, f()]
useState()
returns [true, f()]
The argument to useState
is the initial value of state
Why does it return an Array?
We’re expected to do Array destructuring.
Object Destructuring
const person = {
name: "Joe",
age: 42
}
const { name, age } = person
Array Destructuring
const [ value ] = [ "No", f()]
value // => "No"
You can name value
, whatever you want! (a difference between object destructuring)
Note: When using useState
we no longer need to initialize state as a full object. It can be a primitive value.
Changing state
The f()
returned from useState
is the function that update the state you just set. Usually the name of this function is set
+ “name of whatever state was set.
const [count, setCount] = useState(0)
...
// bad
<button onClick={() => setCount(prevCount => prevCount + 1)}>Change!</button>
...
// better
function increment() {
setCount(prevCount => prevCount + 1)
}
...
<button onClick={increment}>Increment</button>
If your state is going to be a big Hash or Array, you need to provide the entire object when updating state. Hooks don’t do the nice thing that setState
did, of just updating the part of state that changed. You can use object spread and it’s not bad.
You can also just make multiple states— wait what does that EVEN mean?
const [count, setCount] = useState(0)
const [answer, setAnswer] = useState("Yes")
use Effect
Hook into a component’s lifecycle method.
Replacement for
componentDidMount
componentDidUpdate
componentWillUnmount
Hook that let’s produce side effects of our component.
Anything that reaches outside the Component to do something:
- Network request
- DOM manipulation
- Even listeners or timeouts and intervals
useEffect(callback, Array)
- typically update state in callback function. The values in the Array are the state values that useEffect
will “watch” and trigger the callback to run. If Array is empty, useEffect
acts like componentDidMount
and only runs once.