Intro
React is a JavaScript library that’s used to build user interfaces for dynamic websites, and is used by several popular Web frameworks. Created by Facebook in the late 2010s.
This document will cover the essentials of the React library, the details regarding project set up depend on your Build tool and/or framework. React’s website encourages using a Web framework instead of just using React by itself.
Components
These are independent pieces of the UI that can be reused and combined to constitute larger components or even the entire page/UI.
All visible parts of a page, such as buttons, inputs, and even entire pages.
Functional Components
These are reusable functions that return a markup used to describe a DOM element. But not HTML, they return JSX which is JS in disguise… Example: creating a button.
function MyButton() {
return (
<button>I'm a button</button>
//' is the escape char for an apostrephe
);
}
Nesting it in another component:
export default function MyApp() {
return (
<div>
<h1>Welcome to my app</h1>
<MyButton /> // Here our function that returns a button component is nested in this component.
</div>
);
}
React component names must always start with a capital letter, while HTML tags must be lowercase.
JSX - Embedding JavaScript in the Markup
JavaScript XML or JSX is a way to write HTML with some JS functionality, such as data mapping, loops, passing variables, etc.
Meaning JSX allows dynamic JS values in the elements seamlessly using curly braces {}
.
JSX attributes are written in camelCase, for example <button class="ugh">
becomes <button className="ugh">
.
The browser doesn't read JSX so it all gets transpiled to HTML AND JS for it to be understood!
React Fragments
Each component returns a single parent component, so to bundle them together use empty components known as ReactFragment .
<>
<Header/>
<Main/>
</>
Class Components
As the name implies, these are classes that represent a component. Example:
class MyComp extends Component{
render(){
return(
<div>
<h1>Welcome to my app</h1>
<MyButton />
</div>
);
}
}
// With React hooks, the trend is to use functional components instead of these...
Using Components
Since they’re reusable elements living in their own files, you’ll need to import and export them when in need. For example:
function Greeting() {
return <h1>"I swear by my pretty floral bonnet, I will end you."</h1>;
}
export default Greeting;
Nested Components
This seems like a clever idea at first, to nest child components withing their parents. However, this will cause it to create a new child with its own memory address and space every time the parent re-renders.
Instead, pass a function that returns the child to the parent as a prop.
Styles
In React, you specify a CSS class with className
. It works the same way as the HTML class
attribute.
For example: <img className="avatar" />
this adds the avatar class to this image component and simply write the CSS in your style sheet.
URL Routing
The React Router
is what empowers using a single HTML template and swapping the components. What resembles travelling to another URL is handled by the Router.
The Router ensures that the page is in sync with the URL the user goes, by serving different components based on the URL.
It’s used in the utmost parent component, often called App
, like so:
import {BrowserRouter as Router, Route} from 'react-router-dom'
function App(){
return(
<div>
<Router>
<Route path='/' element={<Home/>} />
<Route path='/about' element={<About/>} />
<Router />
</div>
);
}
Props (as in properties)
Used to pass data into a component from another component, often from a parent to child… ==// This becomes intuitive when you remember we’re using functional components!==
- Create a name for the component. For example,
<Greeting text={'sup}
- Use prop by passing it to a function as an argument and take it from the component:
function Greeting(props){
return <h1>{props.text}</h1>
}
Once a prop’s passed in, we can use it as any other parameter within the component’s scope. Can also access child props of components in other components. This promotes ReactComposition
Prop Drilling
Passing down props from parent to child across several nested layers, but is better avoided.
Key props
Used to identify components uniquely for doing things like using map functions.
Example: <Component key={1}>
; these keys are either a unique number or string. Sometimes the index from the map
function is good enough, emphasis on sometimes.
When rendering a list of data in components, pay great attention to the key prop! Each item in the list should contain the key prop, and must be unique, otherwise React will throw errors at you as it’s needed to identify which items have been changed, added, or removed. ==// This ties into the Virtual DOM== Example:
<ul>
{items.map(e => (
<li key={e.id}>{e.value}</li>
))}
</ul
But if you don’t have IDs then do NOT use indices as a key! Instead, use crypto.randomUUID()
to give each item a unique ID.
Example:
const item = [{...}, {...}, {...}, ...];
const data = items.map(item => {
return {...item, id: crypo.randomUUID()}
})
Don't call
crypto.randomUUID()
in the JSX mapping of list items, that'd cause React to recalculate on each render, destroying and recreating the list items.
Children Props
We can pass other components (children) as props to another (parent). example:
function Parent(props){
return(<div>{props.children}</div>);
}
This looks like:
<Parent>
<Child />
</Parent>
This promotes Composition which is fancy talk for optimal organization of components…
State
This is a big one when talking about React, it’s a foundational pillar.
In the olden times, when Class components where more popular, the state was managed as a JavaScript object that’s initialized in the constructor()
of the component.
class MyComp extends Component{
constructor(props){
super(props);
this.state = {//
};
}
render()//
}
But modernly, this is done using React Hooks such as useState()
and useReducer()
.
// This is what I'm familiar with.
Which is unpacked to the variable representing state and the function to mutate it.
useState()
expects an argument for the initial value for that state variable.
For example:
function MyComp(){
const [likes, setLikes] = useState(0) //starting value, "likes"
// ...
}
The likes
is the state var and setLikes
is the function that updates that variable.
Regular JS variables (let
and const
) can’t be used because the prohibit re-rendering. ==// Remember, the hook returns an array []
of the value and setter not an object {}
!==
Full component example:
function Likes(){
const [likes, setLikes] = useState(0) //starting value, "likes"
const handleClick = () => {
setClicks(likes + 1)
}
return(
<button onClick={handleClick}>
Likes:{likes}
</button>
)
}
Controlled components use state value to have predictable behavior.
Component Life Cycle
Another important concept to understand. React components have a life cycle they go through, and there are three main phases.
Mounting
When being added to the DOM
Updating
When it gets updated while on the DOM, for example a value is changed or another child component is added…etc.
Unmounting
When the component is removed from the DOM.
Class Component Methods
Class components have 3 methods, one for each of the phases, that handle these.
componentDidMount()
: defines what to do when the component is mounted onto the DOM.componentDidUpdate()
: defines what to do when the component is updated.componentWillUnmount()
: defines what to do when the component is being removed from the DOM.
Functional Components
The useEffect()
hook takes care of all three phases.
Its syntax will be revisited later
useEffect(()=>{
// handle the 3 methods
}, [dependencies]);
React Hooks
by now you’ve heard me mention hooks a few times, well let’s get it. React Hooks are exclusive to Functional Components but since they’re the popular choice, almost ubiquitous, these are essential to understand and use!
Hooks are functions that allow hooking into and managing state. They all follow a similar naming convention, useHookName();
They enable us to add State to func components without using class based components, before this func components couldn’t hold state.
Rules
Three rules for hooks:
- Hooks can only be called inside functional components.
- Hooks can only be called at the top level of a component.
- Hooks cannot be conditional
Common Hooks
Despite the numerous built-in hooks in React, the most used two are:
useState()
: set and update the state variables of a component, check State.useEffect()
: handle the 3 life cycle phases as mentioned a above.
React empowers us to build our own hooks!
useEffect Hook
This hook is for adding side-effects, which let us do things during the life cycle phases such as manually changing the DOM, fetching data, etc. Check Effects later on…
It accepts two parameters, a function (lambda) and an optional dependencies array: useEffect(<function>, <dependency>)
.
The useEffect
hook runs on every render, meaning we may trigger a side effect whenever a render takes place…bad. So must include the second parameter.
- No dependencies, it’ll run on EVERY render → infinite loop → BAD!
- Empty array, only runs on first render.
- Non-empty array of dependencies, runs on the first render and when the dependencies change.
Effect Cleanup
Some effects need cleanup to prevent memory leaks, timeouts, subscriptions, event listeners, and other not needed effects should be cleared. Can do this by including a return
function at the end.
Example:
useEffect(() => {
let timer = setTimeout(() => {
setCount((count) => count + 1);
}, 1000);
return () => clearTimeout(timer)
}, []);
useContext Hook
Allows a component to subscribe to a specific context within the app.
function MyComp(){
const theme = useContext(ThemeContext);
//...
}
Now MyComp
has subscribed to the ThemeContext
and can tap into it for its data.
useRef Hook
Ref(rences) are a way for a components to hold information NOT used for rendering, example would be a DOM node (identifiable element) or a timeout ID.
Unlike state variables, when a Ref updates it doesn’t trigger a re-render, so not worth passing to useEffect
.
The React docs describe them as “escape hatches” to leave the React paradigm when interacting with other systems such as the DOM directly or other browser-based APIs.
useRef
declares a ref. You can hold any value in it, but most often it’s used to hold a DOM node.
useImperativeHandle
lets you customize the ref exposed by your component. This is rarely used.
function Form() {
const inputRef = useRef(null);
// ...
}
The useRef
hook returns an object called current
and to access the persistent value: input.current
.
useMemo Hook
Utilizes memoization to remember previously calculated values. Great for values that don’t need to be reevaluated on each render. Example:
const [count, setCount] = useState(0);
const myValue = useMemo(()=> expensiveCalculation(count), [count]);
Breakdown:
- The hook expects two arguments:
- A function/callback to do the evaluation. In this case it’s
expensiveCalulation
function. - Dependencies. Just like
useEffect
, we can pass an array of dependencies; when they change the hook will run
- A function/callback to do the evaluation. In this case it’s
- Now
myValue
is memoized and its previous value be remembered.
State Management
While each component can manage its own state within itself and we can write the logic to do that, at some point we’ll need to manage the Global state of the web app, especially to make data readily available across multiple components. E.g. data for a logged in user.
There are two main ways of doing this:
- The built-in Context API
- Third Party packages like Redux and others. Leveraging these we can create a global stage for our state that can be used by multiple components while promoting SingleSourceOfTruth and avoiding Prop Drilling.
Virtual DOM
React creates a virtual DOM of the real DOM and when the components are updated, it’s the virtual DOM that is updated.
Steps
- State change: when a change occurs, update virtual DOM.
- Compute Difference: the difference between the DOMs is evaluated.
- Re-render: now updates the real DOM with the new changes without reloading the rest of the unchanged UI. This whole process is called ReactReconcilliation and is why React isn’t slow as rocks.
Event Listeners and Handling
Handle the various events that happen. The common ones are:
onClick
onSubmit
onChange
Similar to plain JS, it’s inline we pass in the function to do it.
<li onClick={doCoolThings}>Cool</li>
Alerting a user
Add the onClick
prop to a button (or other component) then set that prop to a function that will handle the alert and show it to the user.
Forms
React handles forms different that plain HTML form elements, since the component holds its state inside it. Whereas HTML elements like <input>
, <select>
, and <textarea>
hold and update their own based on the user’s input on the page
So instead, we add the previously mentioned event listeners to these elements inline, and then pass the appropriate functions. Example:
<form onsubmit={submitForm}>
<input type="text" onchange={updateStateValue} value={stateValue}/>
<input type="submit"/>
</form>
Conditional Rendering
Allows us to write conditional statements to check predicates and render accordingly. Two ways to do so
- With the
&&
operator:
{somePredicate && <p>condition met</p>
- Or a more elaborate with
else
clause:
{somePredicate ? (
<p>condition met</p>
) : (
<p>condition NOT met</p>
)
}
React Purity
Can be summarized as *“Same component with the same input should produce the same output”*and not change pre-existing objects or variables before rendering Example of unpure component:
let count = 0;
function Yeah(){
count = count + 1; // VERY BAD
return(<h1>Yeah Count: {count}</h1>);
}
<Yeah /> // 2
<Yeah /> // 4
This causes different outputs when the component is reused…
Explanation
You’d think it’s:
- First render →
count = 1
- Second render →
count = 2
But instead you’re seeing 2 and 4.
This happens because of how React function components work when JSX is evaluated:
- When you write
<Yeah />
in code (not inside an actual React render tree but just sequentially like the example), React actually callsYeah()
immediately to resolve what JSX it should render… - Each call to
Yeah()
incrementscount
by 1. First<Yeah />
→count
goes from0 → 1
→ returns<h1>Yeah Count: 1</h1>
. - But then, if those
<Yeah />
calls are inside another component’s render function, React may call that render function more than once. // Like in strict mode, dev mode, or reconciliation purposes.
React StrictMode
StrictMode is enabled by default in development, components (and any functions directly in JSX) are invoked twice intentionally to help detect unsafe side effects. So it ends up evaluating like so:
- First
Yeah()
call → increments to1
StrictMode
double-invoke → increments again to2
→ Result:"Yeah Count: 2"
- Same happens for the second usage of
<Yeah />
:- Call increments to
3
StrictMode
double-invoke increments again to4
→ Result:"Yeah Count: 4"
- Call increments to
Key Takeaway
Effects
Refers to code that reaches out of our React code to interact with the browser API or perhaps make requests to a server.
If not done in an even handler, then often in the useEffect
hook.
API Requests
When making API calls, there are no standards but there’s some best practices that increase readability, maintainability, etc. Read APIs in React.
Context
Context allows passing data from distant parent components to child component without prop drilling. In other words, when needing to pass props down a chain of nested components, instead of passing the prop down one step at a time through components that don’t need it, we use ReactContext. This lets us Jump the data where it needs to go.
- Create context
const AppContext = createContext();
- Wrap Provider component
<AppContext.Provider>
<App/>
</AppContext.Provider>
- Put data on value prop:
<AppContext.Provider value="Bob"
- Get data with
useContext
function Title(){
const text = useContext(AppContext)
return <h1>{text}</h1>
}
We’re accessing the data in the <App />
component’s context, and since it’s the App it’s available all throughout…
Portals
…
Suspense
…
Error Boundaries
…
Screens (React Native)
A React Screen is not a specific concept in React itself. Instead a “screen” refers to a distinct view or page within a mobile application.
In React Navigation, a screen represents a route in a navigator. It is typically defined using the Stack.Screen
component and contains the following elements:
- A unique name to identify the screen
- A component prop that specifies the React component to render for that screen
- Optional configuration settings, such as the title to display in the header
Here’s a basic example of how a screen is defined in React Navigation:
<Stack.Screen
name="Home"
component={HomeScreen}
options={{title: 'Welcome'}}
/>
Screens in React Navigation receive a navigation
prop, which provides methods for navigating between different screens in the app. This allows developers to create a seamless navigation experience for users.
The react-native-screens
library provides native primitives to represent screens instead of plain React Native View components. This approach offers better operating system behavior and improved performance for screen transitions.