-
Notifications
You must be signed in to change notification settings - Fork 0
React
herougo edited this page Apr 29, 2025
·
33 revisions
- (YouTube Playlist by codevolution): https://www.youtube.com/playlist?list=PLC3y8-rFHvwgg3vaYJgHGnModB54rxOk3
- HTML, CSS, and JavaScript fundamentals
- ES6 - let and const, arrow functions, template literals, default parameters, object literals, rest and spread operators, and destructuring assignment
- JavaScript - 'this' keyword, filter, map, and reduce
let myFunction = (a, b) => a * b; // arrow function
let text = `Welcome ${firstName}, ${lastName}!`; // template literals (use back-ticks)
const candidate = { // object literal
title: "Mr.",
firstname: "Tanka"
};
const candidate2 = {...candidate, firstname: "Guy"}// spread operator
function sum(...theArgs) { // rest operator
let total = 0;
for (const arg of theArgs) {
total += arg;
}
return total;
}
var x, y;
[x, y] = [10, 20]; // destructuring assignmentnpx create-react-app <project_name>
cd <project_name>
npm start
Creates a project with the following (and more):
- public/index.html
- shouldn't need to modify this file
- public/manifest.json
- package.json
- contains dependencies
- src
Vite is a frontend build tool and development server designed for speed and simplicity in modern web development.
npm create vite@latest my-react-ts-app -- --template react-ts
npm install
npm run dev
Explanations of Boilerplate Files
-
vite-env.d.ts: helps TypeScript recognize the custom environment variables prefixed with VITE_ that you define in your .env file -
tsconfig.node.json: used by vite itself -
tsconfig.app.json: used by your app in thesrcfolder
- You can create class and functional components
- Tip: use functional components as much as possible
- why?
- Easier to understand (no added complexity of lifecycle methods, state, and this binding)
- Better performance
- Easier to test
- source: https://www.geeksforgeeks.org/why-it-is-recommended-to-use-functional-components-over-class-components/
- why?
- install the "JS JSX Snippets Extension" in VS Code
- rfc + tab -> functional component
- rcc + tab -> class component
Use
<Welcome name="Bruce">
<div>child</div>
</Welcome>Definition
note the following
- this code should set the greeting from "Welcome" to "Hello" when a button is clicked
- access of
props.name - state of greeting (constructor and this.state.greeting)
- never modify
this.stateoutside the constructor (use this.setState instead) - button onClick methods
- avoid approach 3 because of performance issues
- react docs recommend approach 4 or 5
import React, { Component } from 'react';
class Welcome extends Component {
constructor() {
super()
this.state = {
greeting: 'Welcome'
}
this.clickHandler2 = this.clickHandler2.bind(this)
}
clickHandler1() {
const callback = () {
console.log(this.state.greeting);
}
this.setState({
greeting: 'Hello'
}, callback)
}
clickHandler2() {
this.setState({
greeting: 'Hello'
})
}
clickHandler3 = () => {
this.setState({
greeting: 'Hello'
})
}
render() {
return (
<div>
<h1>{this.state.greeting} {this.props.name}</h1>
<button onClick={() => this.clickHandler1()}>Say Hello Instead 1</button> {/* works */}
<button onClick={this.clickHandler1}>Say Hello Instead 2</button> {/* doesn't work */}
<button onClick={this.clickHandler1.bind(this)}>Say Hello Instead 3</button> {/* works */}
<button onClick={this.clickHandler2.bind(this)}>Say Hello Instead 4</button> {/* works */}
<button onClick={this.clickHandler3}>Say Hello Instead 5</button> {/* works */}
{this.props.children}
</div>
);
}
}
export default Welcome; // import using 'import Welcome from "Welcome"'Use
<Hello name="Bruce">
<div>child</div>
</Hello>Definition
import React from 'react';
const Hello = (props) => { // can omit props on this line if not needed
// alternatively use below
// const {name} = props;
return (
<div>
<h1>Hello {props.name}</h1>
{props.children}
</div>
);
}
export default Hello;- (See above)
- props are immutable in a component
When the state depends on the previous state and you want to call setState, pass a function to setState. This is because otherwise, React might group multiple calls together and do them all in one go.
...
increment() {
const countIncrementer = (prevState, props) => ({
count: prevState.count + 1
})
this.setState(countIncrementer);
}
...Parent
<ChildComponent greetHandler={this.greetParent} />Child
...
<button onCLick={props.greetHandler}>Greet Parent</button>
...- key is needed when rendering lists due to how React checks to see what needs to be re-rendered
const personList = persons.map(person => <Person key={person.id} person={person} />)
return <div>{personList}</div>When acceptable to use index as key?
- Items do not have a unique ID
- List is static (won't change)
- List will never be reordered or filtered
myStyle.css
.primary {
color: orange
}appStyles.module.css
.error {
color: red
}Stylesheet.js
...
import './myStyle.css' # children components also see this (can lead to conflicts)
import styles from './appStyles.module.css'
const heading = {
fontSize: '72px',
color: 'blue'
}
function StyleSheet() {
return <div>
<h1 className='primary'>Stylesheets</h1>
<h1 style={heading}>Inline</h1>
<h1 className='styles.error'>Modules</h1>
</div>
}
...Can use textarea, select
...
handleUsernameChange = event => {
this.setState({
username: event.target.value
})
}
...-
Mounting: when an instance of a component is being created and inserted into the DOM
- constructor, static getDerivedStateFromProps, render, and componentDidMount
-
Updating: When a component is being re-rendered as a result of changes to either its props or state
- static getDerivedStateFromProps, shouldComponentUpdate, render, getSnapshotBeforeUpdate, and componentDidUpdate
-
Unmounting: When a component is being removed from the DOM
- componentWillUnmount
-
Error Handling: When there is an error during rendering in a lifecycle method, or in the constructor of any child component
- static getDerivedStateFromError, and componentDidCatch
Mounting
| Method | Use | Do | Don't |
|---|---|---|---|
| constructor | - initialize state - bind event handlers |
- cause side effects (e.g. HTTP requests) | |
| static getDerivedStateFromProps | when state depends on changes in props over time | - set state | - cause side effects |
| render | only required method | - read props and state and return JSX | - change state, make Ajax calls, or interact with the DOM |
| componentDidMount | invoked after a component and all of its children components have been rendered to the DOM | - cause side effects |
Order of Methods
- Parent Constructor
- Parent getDerivedStateFromProps
- Parent render
- Child Constructor
- Child getDerivedStateFromProps
- Child render
- Child componentDidMount
- Parent componentDidMount
Updating
| Method | Use | Do | Don't |
|---|---|---|---|
| static getDerivedStateFromProps | called every time a component is re-rendered | - set state | - cause side effects |
| shouldComponentUpdate | self-explanatory | - performance optimization | - cause side effects |
| render | (see previous table) | (see previous table) | (see previous table) |
| getSnapshotBeforeUpdate | called right before changes from the virtual DOM are reflected in the DOM | - capture some info from the DOM | |
| componentDidUpdate (prevProps, prevState, snapshot) | called after the render finished | cause side effects |
- static getDerivedStateFromProps is rarely used in the update lifecycle
Error Handling
| Method | Use | Do | Don't |
|---|---|---|---|
| static getDerivedStateFromError(error) | ? | ||
| componentDidCatch(error, info) | ? |
Unmounting
| Method | Use | Do | Don't |
|---|---|---|---|
| componentWillUnmount | invoked immediately before component is destroyed | cancel network requests, remove event handlers | call setState |
- when you want to have a component with multiple elements, but don't what to wrap them in a div
...
return (
<React.Fragment> {items.map(item => ...)} </React.Fragment>
)Equivalent syntax for convenience
...
return (
<> {items.map(item => ...)} </>
)import React, { PureComponent } from 'react'
class PureComp extends PureComponent {
...
}
export default PureCompRegular component
- does not implement the shouldComponentUpdate method (always return true by default)
Pure Component
- implements shouldComponentUpdate with a shallow props/state comparison
Tip
- use regular components most of the time unless there is a performance hit
- equivalent of Pure Component for functional components
import React from 'react'
function MemoComp({name}) {
...
}
export default React.memo(MemoComp)- allows you to interact with the DOM
import React, { Component } from 'react';
class RefsDemo extends Component {
constructor() {
super()
this.inputRef = React.createRef()
}
componentDidMount() {
this.inputRef.current.focus()
}
clickHandler = () => {
alert(this.inputRef.current.value)
}
render() {
return (
<div>
<input type="text" ref={this.inputRef} />
<button onClick={this.clickHandler}>Click</button>
</div>
);
}
}
export default RefsDemo;Note
- can attach ref to a component and use that ref to call component methods (e.g. this.compRef.focusMe())
- refs can't be attached to functional components
- way to pass Refs to functional components
- rarely used
const FRInput = React.forwardRef((props, ref) => {
return (
<div>
<input type="text" ref={ref} />
</div>
)
})- Normally, components are rendered under the "root" node defined in the HTML
- Portals allow you to render components in nodes outside this "root" node
class App extends Component {
render() {
return (
<div className="App">
<PortalDemo />
</div>
)
}
}import ReactDOM from 'react-dom'
function PortalDemo() {
return ReactDOM.createPortal(
<h1>Portals demo</h1>,
document.getElementById('portal-root')
)
}-
Error Boundary: class component that implements one or both of the following lifecycle methods.
- static getDerivedStateFromError(error): used to render a fallback UI after an error is thrown
- componentDidCatch(error, info): used for logging error information
...
this.state = {
hasError: false
}
}
static getDerivedStateFromError(error) {
return {
hasError: true
}
}
render() {
if (this.state.hasError) {
return <h1> Something went wrong</h1>
}
return this.props.children
}<ErrorBoundary>
...
</ErrorBoundary>- a pattern where a function takes a component as an argument and returns a new component
- can be used to add new props to the input component
- used for code reuse
const NewComponent = higherOrderComponent(originalComponent)// withCounter.js
import React from 'react'
const withCounter = WrappedComponent => {
class WithCounter extends React.Component {
constructor(props) {
super(props)
this.state = {
count: 0
}
}
incrementCount = () => {
this.setState(prevState => {
return { count: prevState.count + 1 }
}
}
render() {
return (
<WrappedComponent
count={this.state.count}
incrementCount={this.incrementCount}
{ ... this.props} /* Please remember this!! */
/>
)
}
}
return WithCounter
}
export default withCounter
// ClickCounter.js
import React, { Component } from 'react'
import withCounter from 'withCounter'
class ClickCounter extends Component {
render() {
const { count, incrementCount } = this.props
return (
<button onClick={incrementCount}>Clicked {count} times</button>
)
}
}
export default withCounter(ClickCounter)
// App.js
...
<ClickCounter />
...- render prop: refers to a technique for sharing code between React components using a prop whose value is a function
// App.js
...
<Counter
render={(count, incrementCount) => (
ClickCounterTwo count={count} incrementCount={incrementCount} />
)}
/>
...
// Counter.js
import React from 'react'
class Counter extends React.Component {
constructor(props) {
super(props)
this.state = {
count: 0
}
}
incrementCount = () => {
this.setState(prevState => {
return { count: prevState.count + 1 }
}
}
render() {
return (
<div>
{this.props.render(this.state.count, this.incrementCount)}
</div>
)
}
}
export default Counter- Context: provides a way to pass data through the component tree without having to pass props down manually at every level
// userContext.js
import React from 'react'
const UserContext = React.createContext() // can use React.createContext('default value')
const UserProvider = UserContext.Provider
const UserConsumer = UserContext.Consumer
export default { UserProvider, UserConsumer }
// App.js
...
<UserProvider value="Vishwas">
<AncestorComponent />
</UserProvider>
...
// DescendentComponent.js
render() {
<UserConsumer> /* pass in function as the child */
{username => {
return <div> {username}</div>
}}
</UserConsumer>
}
...ContextType ??????? (Omitted)
import axios from 'axios'
axios.get('...')
.then(response => {
console.log(response)
})
.catch(error => {
console.log(error)
})