React Notes¶
Topics¶
Section 1: Introduction¶
–
Section 2: React Key Concepts¶
Before React¶
We first had complex JS
Then we had jQuery
We got AngularJS. Good but messy as all the operations were “functional”.
Why do we need react?¶
React was the first to introduce “declarative” definitions of state, and “map” it to a state of the user interface.
Angular and other frameworks quickly followed suit, but React already had the early mover advantage that it needed.
Basic concepts¶
- React will do all DOM manipulations
- Re-usable components
- Unidirectional data flow - from state to top component and then to child components
- React is only for the UI
Important skills for a React Developer¶
- Decide what the components will be.
- How granular should they be
- Decide the state (and where it will live)
- What to change based on change of state
- Performance requirements - updating constraints
Section 3: React Basics¶
NVM and NPM¶
nvm
- Node Version Manager (manage different versions of node simultaneously) - yarn is an alternativenpm
- Node Package Manager - manage different packagesnpx
- install a package, run the command, and remove the package instantly
- Lesson 13 - 19: Basic settings
Create React App¶
- Lesson 20:
create-react-app
makes life easier by creating a boilerplate application for us. It has all the necessary folder structure and git
How React manipulates the DOM¶
-
Lesson 21:
react
package acts as the frameworkreact-dom
is to work with the DOM using the react framework.react-native
is for native components, etc.- react finds an element in the body, and renders the entire apps inside that element
<div id="root"></div>
inpublic/index.html
const root = ReactDOM.createRoot(document.getElementById('root'));
insrc/index.js
- there can be sister non-react elements in
public/index.js
in the application
Builds, babel, and webpack¶
- Lesson 22:
build
-ing a script: creates an optimised production build, and puts all into thebuild
folder - generally uses babel and webpack (and some others)- babel - Translate JS to basic JS that most web browsers can parse
- webpack - breaks the JS into smaller chunks that are navigationally separate (for example, the js of homepage should be in the homepage). It will manage all.
How JSX works¶
- Lesson 23:
- React JSX is basically HTML, but with functions that can return more HTML inside the HTML at runtime
Ejecting Components¶
-
Lesson 24:
- Question:
How is ejecting a problem? Say, I usenpx create-react-app hello
and then start working on my project.
Now consider two scenarios.
Scenario 1>> I do not eject, and there is a new way of working with the “hidden files” (say, the web pack configuration).
Scenario 2>> I eject.
How are the two scenarios different? How will my project be updated in scenario #1, and what benefit does scenario #1 have over scenario #2?
I used
npx
, so I do not event have create-react-app installed. - Question:
-
Lesson 25: Classes vs hooks - hooks are new and slightly complex
- Lesson 27-28:
- When is react rendering and re-rendering components and which ones is the most important.
- Functional Components and Class Components
- Lesson 29-31:
- When changing the state, always use
setState()
because react will only update if the object in memory is a new objectsetState()
works with shallow merge - all the keys that are not provided to the object passed as parameter are not touched
- When changing the state, always use
Creating multiple elements of one component¶
- Lesson 32:
map
- iterates over a list of objects, and created JSX elements from each of the objects -
Lesson 33-34: Key in a list
- Elements in a react list must have a
key
property, that denotes the unique identifier for the list element. -
React will use this “key” to understand what was updated and which element needs to be updated, instead of updating all the elements/nodes in the list
- Elements in a react list must have a
-
Lesson 35: Data source for applications
- Earlier architectures relied on multiple calls to get different web pages
- How SPAs work
- One request (theoretically) to get the code for the entire website
- Of course authentication and other requests are made to servers.
- One request (theoretically) to get the code for the entire website
Life Cycle Methods¶
-
Lesson 36:
-
Life Cycle Methods - Events during the life cycle of a component, like getting mounted (
componentDidMount()
- details at this link) or getting updated (componentDidUpdate()
) and many more-
Take a look at the API reference at
-
-
Use native
fetch
to easily get data from a URL - Promises can be chained
-
Brief Order of Execution¶
- Lesson 37:
- Order of execution of the code elements in our example
constructor()
runs firstrender()
componentDidMount()
render()
- run again ascomponentDidMount()
changed the state
- Order of execution of the code elements in our example
- Lesson 38
Functionality before styling - how to think¶
- Lesson 39:
- Always work on functionality before working on styling for frontend projects
- Break down the functionality of the entire app into smaller components
- Monster rolodex has
- Input field
- Data is filtered on each change
- X mark to delete data in the box
- Cards
- Input field
- Monster rolodex has
Protected Keywords¶
-
class
is a protected keyword in JS, so JSX components useclassName
instead ofclass
from their HTML counterparts-
When input fields are changed, a
change event
is triggered, that can be handled by a change handler - classic pub-sub mechanism-
The handler can be defined as the
onchange
attribute -
The
event
has[event.target](http://event.target)
that defines the state of the target of the event, which is the element that was modified - The target for a input box has
value
, that shows the complete value of the text box AFTER the event was triggered - The solution to the filtering
- Think of the state. The change in the text field changes the “state” of the application, so the
searchString
should be in the state - When the state is changed, the list in the state should be filtered
-
The original list should not be touched, as we might need to get that back - do not mutate the original list
class App extends Component { constructor() { console.log('constructor-begin'); super(); this.state = { monsters: [], **filterString: ''** }; } // componentDidMount() { console.log('componentDidMount-begin'); fetch('https://jsonplaceholder.typicode.com/users') .then((response) => response.json()) .then((users) => { this.setState(() => { return { monsters: users }; }, () => { console.log(this.state); }) }); } render() { console.log('render-begin'); return ( <div className="App"> <input className='search-box' type='search' placeholder='search monsters' onChange={ (event) => { console.log(event.target.value); this.setState(() => { return { filterString: event.target.value }; }); return null; } } /> { this.state.monsters.filter((monster) => { return monster.name.toString().toLowerCase().includes(this.state.filterString); }).map( (monster) => { return <div key={monster.name}> <h1>{monster.name}</h1> </div> }) } </div> ); } }
- Think of the state. The change in the text field changes the “state” of the application, so the
-
-
-
Lesson 40-42:
- Immutability is a good practice - Do not change anything in the state in a way that changes the state. Some useful functions that respect this are
map
filter
reduce
- Rest of the lecture is basic and useless
- Immutability is a good practice - Do not change anything in the state in a way that changes the state. Some useful functions that respect this are
- Lesson 43:
- Anonymous functions are created in runtime and then throws away. Re-creating them again and again consumes time.
- Therefore, the
render()
should not contain anonymous functions if the functions are not changing themselves
- Therefore, the
- Anonymous functions are created in runtime and then throws away. Re-creating them again and again consumes time.
- Lesson 44-45: Components
- Theory
- Components should be more and more general-purpose
- One component should try to have one logically coherent job/role/responsibility
- Process
- Put components in separate folders in
src
- As convention, each component folder should be named as
<componentName>.component.jsx
- Must import Component from react to inherit/extend
- return the JSX from the component file
default export
is a cool way to set the default class to export from a file
- Put components in separate folders in
- Theory
- Lesson 46-47:
props
- Stands for “properties”
- Can be used to pass data to child components
- React renders on mount and also whenever props change
- Lesson 47: Component Tree
- Lesson 48: Using props to pass functions
- Lesson 49: CSS in React
- No matter where you import a CSS file, react will build all the css into one CSS files
- So if one CSS in imported into one file, the CSS is applicable to all files
- Isolating CSS can be done with some existing libraries that we will learn later
- No matter where you import a CSS file, react will build all the css into one CSS files
- Lesson 50: CardList component
- Lesson 51-52: Separate the Card Component
- Lesson 53:
-
Lesson 54: LifeCycle Methods - the diagram is here
Components go through the three phases
- Mounting
- Updating
- Un-mounting
- Lesson 55:
- Functional components do not have life-cycles, unlike Class components
- Lesson 56: Pure and Impure Functions
- A function is considered “pure” if the output is an isolated function of the inputs. If it is not, then it is called “impure” function
-
Pure function - Returns the same output given the same inputs
-
Impure function - Depends on “things” other than the inputs (like a variable outside the function
-
Side effects - If a function has an effect (changes something) that is outside it’s explicit scope, it is called a side effect. Side effects are also considered “impure”.
-
Hooks are generally impure functions, and they create side effects
- Lesson 57-58: Hooks and functional components
useState(object)
- returns array with two values - a value and its “setter()
”. The “value” can be initialised, and the functional component is re-rendered only when the data in value and the argument to thesetter()
are different- Lesson 59-60:
- If you are stuck in a rendering loop, you need to check if you are changing data during a render, that can trigger another render
- Solution is
useEffect(() => {function to run}, [in case these values change])
for functional components- Can be used to run specific logic only if specific values change between subsequent renders
- Lesson 61-62
- Lesson 63: Some components are double rendered because of the strict mode. Use a browser extension to “grey those out”.
- Lesson 64: DOM and Virtual DOM
- DOM -
- Making changes is expensive computationally (adding and removing nodes cost the most)
- Virtual DOM - A duplicate of the real DOM inside JS
- Computationally cheaper than the real DOM
- Triggering process - Need to understand better
- Lesson 65-66: React and ReactDOM - Can be used as base libraries as well by importing the libraries into a plain vanilla HTML
- Lesson 68:
Section 4: Capstone Project Intro¶
Where to find code if needed at any time during the project:
GitHub - ZhangMYihua/crwn-clothing-v2: Version 2 of Crwn-Clothing!
- Lesson 75: Try to work from outside to inside with boilerplate without styling. Build functionality first.
Section 5: React Routing¶
- Lesson 81
- Lesson 82
- A fragment is useful to group things invisibly. The
<Fragment></Fragment>
is never rendered.- A react component is supposed to render only one parent component. Fragment can be used to group many into one without adding unnecessary “container” component
- The routes are hierarchical, and the routes can be nested
-
An
<Outlet/>
can be used to show the components of the “child” route in the parent route
```jsx
const App = () => {
return (
}>
} />
);
}const NavBar = () => {
return (
This is the NavBar
);
};```
Section 6¶
Section 7: React Context¶
Used for State Management
Sometimes the same data needs to be accessible by many components in the tree, and at different nesting levels.
Context lets us “broadcast” such data, and changes to it, to all components below.
Common examples where using context might be simpler than the alternatives: managing the current locale, theme, or a data cache.
prop drilling¶
prop drilling
- Passing data via props to components that need it through components that do not need it
React context helps avoid this.
But, if you are looking to just avoid prop drilling, Component Composition can be a better solution.
Introduction to Context¶
React context allows us to store data.
Contexts are actually “glorified” functional components that expose their value and their state setter to the child components.
Section 8: Observable Listeners¶
// Lesson 109: Observer Pattern
// onAuthStateChangedListener is a Listener available from "../utils/firebase/firebase.utils" in my project
useEffect(() => {
const unsubscribe = onAuthStateChangedListener((user) => {
if (user) {
createUserDocumentFromAuth(user);
}
console.log(user);
setCurrentUser(user);
});
return unsubscribe; // run unsubscribe when the component is unmounted
}, []);
Section 9: React Context - Deep Dive¶
Context provides a way to pass data through the component tree without having to pass props down manually at every level.
In a typical React application, data is passed top-down (parent to child) via props, but such usage can be cumbersome for certain types of props (e.g. locale preference, UI theme) that are required by many components within an application. Context provides a way to share values like these between components without having to explicitly pass a prop through every level of the tree.
When to Use Context¶
Context is designed to share data that can be considered “global” for a tree of React components, such as the current authenticated user, theme, or preferred language.
When NOT to use Context¶
Context is primarily used when some data needs to be accessible by many components at different nesting levels.
Apply it sparingly because it makes component reuse more difficult.
If you only want to avoid passing some props through many levels, Component Composition is often a simpler solution than context.
Context Providers¶
Lesson 113: Products Context¶
When thinking of providers, think about what might need your provider when deciding where to put it.
A provider can access data from it’s parent provider. So, if a provider needs some data from another provider, the first provider needs to be nested inside the second provider.
Section 10: Firebase Database Storage¶
Section 11: Styled Components¶
Lesson 135-139: Styling Components¶
- We can use JSX files, and use
styled-components
to style a component. - We can style ANY component, be it a standard div or our own components
- Components styled “below” can extend and style a component defined “above” in the style module/file
- Be careful while importing the styled library. Note that there are no curly braces
{}
around the import.
import styled from "styled-components";
Lesson 140: Passing CSS Around¶
To pass CSS around, use a css styled element.
import styled, { css } from "styled-components";
// Define the style
const shrinkLabelStyles = css`
top: -14px;
font-size: 12px;
color: ${mainColor};
`;
// Use the style conditionally, based on a prop, in another component
export const FormInputLabel = styled.label`
color: ${subColor};
${({ shrink }) => shrink && shrinkLabelStyles};
`;
Section 12 - Deploying with Netlify¶
Setup¶
Need to update the Build Command to CI= yarn build
so that the CI environment is used.
Redirects with Netlify¶
Create a file called _redirects
in the public
folder, and ask it to redirect all paths to index.html, and return a status code of 200.
Section 13: Reducers¶
Reducers are a third party state management solution.
The reducer is a pure function that accepts 2 parameters: the current state and an action object.
Depending on the action object, the reducer function must update the state in an immutable manner (?meaning?), and return the new state.
const userReducer = (state, action) => {
return {
...build and return the new state based on the action...
...typically switch case is used...
}
}
Reducers and business logic¶
Do NOT write business logic in the reducer.
The reducer function should only update the state, and not care about “what” to update the state to. The exact values to be set are to be passed to the reducer in the payload.
When to user Reducers¶
Use reducers when one event needs to update multiple values in the state/context.
Migration Strategies from useState()
and setState()
to useReducer()
¶
- identify what is important to track
- check the existing context attributes
- we do not need the functions, just the values
- keep the default values from the context
- To keep business logic separate from the reducers React Udemy Course Notes > Reducers and business logic, think differently at this step to make the actions more generic. This is one of the greatest powers of reducers - maintaining only the state.
Section 14: Reducers¶
Context API VS Redux¶
Accessibility - All the information in the global context can be accessed by all the components in case of redux. But contexts can be used to segregate the components in terms of accessibility to data
Flow of data -
How it works¶
We have only one parent reducer, and the state of the entire application is managed by this one reducer in a centralized manner.
The components fire actions that one or more reducers can choose to either use (to change the global state) or ignore.
The individual reducers are a way to compartmentalize logic, even though all the state is managed by the parent reducer.
The individual reducers update the root reducer, which in turn, is used by the components.
Since all reducers react to all events in redux, it is important to return the original state
as the default
in the switch-case
of every reducer when working with redux.
Middleware¶
Middle-wares are things that are processed before and after the reducer is hit.
They generally enhance the store, for things like logging.
Also see: Createing own Middlewares
Redux root reducer¶
In redux, conceptually, we have only one reducer, called the root reducer.
To break it down to manageable parts, we split it into different reducers, and then use combineReducers()
to combine them.
import { combineReducers } from "redux";
import { userReducer } from "./user/user.reducer";
import { categoriesReducer } from "./categories/category.reducer";
export const rootReducer = combineReducers({
user: userReducer,
categories: categoriesReducer
})
Individual Reducers¶
Each reducer looks like this:
import { CATEGORIES_ACTION_TYPES } from "./category.types";
export const CATEGORIES_INITIAL_STATE = {
categoriesMap: {}
};
export const categoriesReducer = (state = CATEGORIES_INITIAL_STATE, action) => {
const { type, payload } = action;
switch (type) {
case CATEGORIES_ACTION_TYPES.SET_CATEGORIES_MAP:
return { ...state, categoriesMap: payload };
default:
return state;
}
}
The useSelector
hook¶
The useSelector
hook from react-redux
is used to get the value of something from the global state (generally parameterized as state
) when a component is rendered.
It takes a function as a parameter.
Find out
How does the internal code of React pull this off?
General Idea regarding reducers¶
The reducers should hold the most basic form of the data (probably the data from the API, or a basic JSON converted to an object).
Then, we use selector
s to perform different manipulations and get the data in a format that we want.
Lesson 157. Business Logic in Selectors¶
Lesson 158: What Triggers useSelector()
¶
Any changes to the redux store triggers ALL useSelector hooks. All useSelector()
s are fired when the state of the store is changed.
Whether the component is re-rendered is a separate thing.
See How to avoid triggering re-renders in to know how to avoid this.
Create own Middlewares¶
Currying can be used to create our own middlewares.
const loggerMiddleware = (store) => (next) => (action) => {
if (!action.type){
// some middlewares share actions where we do not get a type, so we just pass
return next(action);
}
console.log('type: ', action.type);
console.log('payload: ', action.payload);
console.log('current state: ', store.getState()); // prints the prv state
next(action); // this is synchronous, so the current middleware waits until all the reducers and next middlewares finish
console.log('next state: ', store.getState()); // this prints the updated state
}
Reselect Library - And how to avoid triggering re-renders in¶
We use the reselect
library for this, which used memoization under the hood.
import { createSelector } from "reselect";
const selectCategoryReducer = (state) => state.categories;
export const selectCategories = createSelector(
[selectCategoryReducer], // the output of this array of functions is going to be the input to the next function, in order
(categoriesSlice) => categoriesSlice.categories
);
Section 15: Redux Extended Tools¶
Lesson 167: Redux Persist¶
A library. It can help us in persisting the state of the redux store across different sessions
URL: https://yarnpkg.com/package/redux-persist
Installation: yarn add redux-persist
The information is stored in Browser Local Storage and can be inspected, as the data is a plaintext JS object.
Lesson 168: Redux-Devtools¶
filter functions are a great way to filter out middlewares based on condition.
Everything from here is needed only at scale.
Section 16: Redux Thunk¶
A flavor of asynchronous side effects inside redux.
yarn add redux-thunk
I need to spot where in my application codebase I have async behavior that I want to move into an action driven flow.
When using thunks, we must start tracking errors in the reducers, because now the reducer needs to know.
Why does it need to know now?
It needs to know because thunk’s greatest capability is to be able to parse these states (of processing, success, error, etc) and provide different behavior for different states.
For example, it can be used to show a spinner while an async action is loading.
Section 17: Redux Saga¶
Additional Stuff / Appendix¶
Component Composition¶
Created : 25 mai 2024