Components in React
- This is how components works in React
- Data drives the UI
- To change the UI, we must first update the data
- No two-way binding. Only one-way binding
How to update components
- This is how React updates components, in more details
Think in React
- Decompose the UI into components
- Each component will render itself, based on external and internal data
- Build trees of the components to create app
Describe the result
- React tries to be declarative instead of imperative
- Don't try to modify the display (imperative) (like in jQuery)
- Instead, just describe the result
- React will figure out the changes that need to happen in DOM
<!-- let's describe the following -->
<h3 title="alarm">The alarm goes off at 10:04 AM</h3>
Create components
- Components are functions (or classes)
- Always start with an uppercase letter
- Take a single argument called props
- Components return (or render) a 'React element' (also called 'virtual element')
//Simple Component.
//Starts with uppercase
//takes a single argument
function SimpleComponent(props) {
return null;
}
JSX to the rescue
- React Elements should be created by JSX
- JSX is an easy syntax to create React elements
- looks like HTML (gets transpiled by the Babel transpiler to regular JS)
- Inside of JSX, curly braces switches to JS
// This is the code to display the time ⮯⮯.
function DisplayAlarm(props) {
return (
// JSX is often enclosed in round brackets ()
<h3 title='alarm'>The alarm goes off at {props.alarmTime}</h3>
// curly braces {} switches to JS ⮭⮭
);
}
//Babel converts JSX to either to _jsx() ot React.createElement() ⮭⮭
JSX gets converted to Regular JS
- Browsers don't know how to deal with the JSX.
- JSX is converted to
_jsx()
by the Babel transpiler. _jsx()
is internal, called by Babel. But you can achieve a similar effect usingReact.createElement()
//JSX gets transpiled to the following JS
function DisplayAlarm(props) {
return _jsx(
'h3',
{ title: 'alarm',
children: ['The alarm goes off at ', props.alarmTime],
},
);
}
How React works behind the curtain
- JSX and
_jsx()
are going to generate React elements.- React elements will describe a Virtual DOM (vDom)
- Virtual DOM works quicker than the DOM, because React Elements are simple JS objects.
- React has a 'reconciliation' phase
- Called after a component (and all its children) are generated
- The elements are generated to the virtual Dom (or vDom):
- React compares current elements with the previous elements of the vDom
- Any modification will generate the smallest DOM change possible
- Called after a component (and all its children) are generated
//This is what the JSX converted to the
//raw React elements looks like
function DisplayAlarm(props) {
return {
type: 'h3',
props: {
title: 'alarm',
children: ['The alarm goes off at ', props.alarmTime],
},
key: null,
ref: null,
$$typeof: Symbol.for('react.element'),
};
}
Components create a tree
- You can use components from other components.
- You build a "component tree" to describe your app
function DoubleAlarmClocks(props) {
return (
<div>
{/* call other components */}
<DisplayAlarm alarmTime={props.time1} />
{/* multiple times */}
<DisplayAlarm alarmTime={props.time2} />
</div>
);
}
//all tags in jsx must be closed, or self-closed ⮭⮭
Use props to push data down
- Data coming from a parent component is called "props" (properties)
- Props are pushed from parent to children through JSX attributes
- Props are passed as the argument to the function.
- Props are "destructured" using the destructuring assignment
{}
//We are "destructuring" the props parameter ⮯⮯
function DoubleAlarmClock({time1, time2}) {
return (
<div>
<DisplayAlarm alarmTime={time1} />
<DisplayAlarm alarmTime={time2} />
</div>
);
}
Store data in State
- Modifiable data in React is called "state".
- State is defined by the
useState
function. - Initial values are used the first time we run the compponent (Mount)
useState
returns an array composed of two items- First item is the data value (
state
) - Second is a function to change that data (
setState
).
- First item is the data value (
- NEVER change the state directly
- Intead, use the
setState
function - This will schedule a rerender of the component (and child components)
- Intead, use the
import { useState } from 'react';
//named export ⮭⮭
function Counter({name}) {
const [counter, setCounter] = useState(5);
//set to 5 the first time it's called ⮭⮭
//use counter to read the data
//use setCounter to write the data
//here is function inside a function
//called by the click event ⮯⮯
function increment(evt) {
setCounter(counter + 1);
}
return (
<div>
<h1>
Hello {name}, the count is: {counter}
</h1>
<button onClick={increment}>Add 1</button>
</div>
);
}
// events are wired this way in React: onClick={fn} ⮭⮭
// It is a synthetic event, different than
// a real DOM event: onclick="increment".
// (Notice the difference in casing)
// Use synthetic events whenever possible.
Change the state to rerender
- When state (or props) change, or the parent re-renders, your component updates AUTOMATICALLY.
- You never have to modify the DOM yourself
- If you ever do, then you're not using the proper React way
function Counter({name}) {
const [counter, setCounter] = React.useState(5);
function increment(evt) {
setCounter(counter + 1);
}
//setCounter ⮭⮭ doesn't change the state right away
//it schedules the change for right after the firing
//of all events. Once all scheduled changes are done
//changing the states, those component will now
//"rerender" (along with the child components).
return (
<div>
<h1>
Hello {name}, the count is: {counter}
</h1>
<button onClick={increment}>Add 1</button>
</div>
);
}
Looping over items
- Loop over an array of data with
.map()
- It returns an element for each iteration
- For performance reasons, remember to provide an unique key for each element
const authors = [
{ id: 1, name: 'Jeff' },
{ id: 2, name: 'Bill' },
{ id: 3, name: 'Mary' },
];
function App() {
return <AuthorList authors={authors} />;
}
function AuthorList({authors}) {
return (
<ul>
{
authors.map((author) => {
return <li key={author.id}>{author.name}</li>;
})
//keys ⮭⮭ are used by React for performance
//in reconciliation phase, not part of the DOM
}
</ul>
);
}
Different ways to style
- use CSS files and then set attribute with
className='danger'
- use 'className' ('class' is a reserved keyword in js)
- Set inline styles with
style={{backgroundColor: 'red' }}
- Alternative, you could refer to a global style variable
const dangerStyle={backgroundColor: red}
<p className="danger">Text</p>
<p style={dangerStyle}>Text</p>
<p style={{backgroundColor: 'red', width: '300px', height: 300}}>
<!-- {/* numeric values will be converted to 'px' units */} -->
Use a third-party library for theming and styling
Here is a list of libraries to help you generate css from js.
- Aphrodite
- Emotion
- Glamor
- Fela
- Styletron
- Jss
- Radium
- React-Native-Web
- Styled-Component
- CSS-in-JS (Facebook 2019?)
Use effect
- First argument of
useEffect()
takes a function.- Runs right after reconciliation and painting the screen.
- Inside of this callback, you perform async functions and side effects
- Manipulating the DOM (read or write) is a side effect
- Network calls are async functions
- The second argument is a "change array" of dependencies
- Effect gets called only when an item in the array is modified
- Code should include in array any var that might change.
- Put null or nothing to execute after all renders
- Put an empty array [] for the effect to be called just once, right after the first render (when React "mounts" the component)
- Effect gets called only when an item in the array is modified
Code sample of useEffect
import { useState, useEffect } from 'react';
const url = 'https://randomuser.me/api/';
function DisplayUser({id});
useEffect(() => {
fetch(`${url}?seed=${id}`)
.then((response) => response.json())
.then((data) => setData(data.results[0]));
}, [id]);
// useEffect ⮭⮭ is called after each render
// where id was modified (by the parent component)
//we return the render result ⮯⮯
return (
<div>
{data ? ( // if no data, show a "no data" msg
<p>
<img src={data.picture.thumbnail} />
{data.name.first}
</p>
) : (
<p>No data (yet)!</p>
)}
</boxed>
);
}
Returned effect: Cleanup
- The returned function is cleanup. it is called either:
- Just before the next effect is called, but after React rendered
- Just after the component is destroyed (when react "unmounts" the component)
- Makes it easy to subscribe and unsubscribe to stuff
useEffect(() => {
chatSubscribe(friendId);
return function cleanup() {
chatUnsubscribe(friendId);
};
}, [friendId]);
//Each time the chat is re-rendered, if friendId was
//modified, then we unsubscribe to the previous chat,
//just before subscribing to the new friendId. The magic
//of "closures" will ensure the old friendID gets
//unsubscribed, not the current one.
React Academy
To get the latest version of this document, visit the handout section of ReactAcademy.live.