A javascript library for building user interfaces
- Javascript vanilla can be legnthy and react can help simplify.
- React is all about the components. Making reusible parts
- Define visually what you want at the end desired State
- Makes rich interfaces easier. Declarative component focused approach
- SPA Single Page Application
- Beware that alot of next gen es6 features will need to be setup to run in browser
-
Trait of a component is that its reusable (dry)
-
Separation of concerns dont do many things in one place 1 specific task
-
Split lots of code into many functional pieces
-
Made up of three parts: Html, CSS, Javascript
-
Declarative aproach: Always defining end/target state React figure out the actual javascript dom instructions
-
Imperative: giving clear step by step instructions
-
Build on custom html elements
-
Remember Javascript is dynamically typed
-
React Code will be transformed and optimized under hood
-
index.js is first to run
-
typically 2 react packages: react & react Dom (dom /)
-
we import react dom and create root tag jsx which reads from index.html
-
if its inner js files you can ommit the .js on import
-
Component is a custom html element
-
You ultimatly end up building componenent trees. Where only top level component is rendererd
-
PascalCase for files
-
A component in react is just a javascript funciton
- Need to use proper nesting in jsx
- one root element per return statement
- Add css directly into components folder
- We now need to inject the css into component js
- We dont use class=() attr need to use
className={}
- Remember componenets are re-useable
- Can define some variables inside function then add to jsx
{expenseDate}
- Props are simply properties or attributes
- Prop as tag attributes look like this
title={}
- These are passed as fn params fn(props)
- Props works with key => value pairs
- Props make compononents configurable and resuseable
- better for dates to create separate const
- Breaking things down is a feature of react
- NB if there is nothing to inbetween a tag you can simply have an autoclose tag
<ExpenseDate />
- Data must exist in parent elements in order to be passed on
- Composition is combining components, Your an artist!
- We can also create wrappers/containers essentially. Rather than just specific tags/components. Where we can pass content between the opening and closing tags
- Eg: Background container, Body Container, Card
- One thing that gets passed regardless whether you declare or not is
{props.children}
<div className="card">{props.children}</div>
- Dynamic class:
const classes = 'card ' + props.className;
making dynamic wrapper. Often gets rid of duplication
-
JSX doesnt get interpreted by the browser, whats renderd is barely readable
-
Previously it was always required to
import React
-
JSX is just syntactic sugar
return (<div></div>)
. An alternative to this is pure jsreturn React.Element('div',{attributes},{content},{content},{content})
-
The below example highlights the necesity for nesting components. As the the third param requires
return React.createElement( 'div', {}, React.createElement('h2',{},"Lets Get Started"), React.createElement(Expenses,{items:expenses}) //Pass just component and props )
- We could further break our structure and separate them by concerns. Expenses (Business / Feature) / UI (Template)
const Expenses = (props) => {
- Just to beaware you may come accross this in wild
- React to events from the user, clicks content capture
- We can listen to all DOM events Button Dom
- Listner: Simply add a prop
onClick={clickHandler}
and link to a function could also be an inline (anonymous) arrow function - Were just pointing to the function no need for
()
otherwise js will do at runtime - A convention is using "Handler" in the name
-
To be able to change variables/props we need to use state
-
If we have a variable in a function that changes react does not care: code executes but the overal function does not change. Function is not retriggerd after first render
-
Remember we just dealing with functions that output JSX. And we need to call it.
- React calls any functions that returns JSX
- Returns any functions within that JSX After rendering react will not re-render/re-evalutated (unless we use state)
-
To get started with state we need to use a named import import this from react lib
{useState}
-
Allows us to define which elements of a component can be updated
-
Simply this hook inside your function useState() no nesting. You just call directly top level main function
-
State needs a default
useState(props.title);
-
No assignment using
=
simply function call -
Pass a the prop and a new method via destructuring [title,setTitle]
-
Use Const
let [title,setTitle] = useState(props.title); const clickHandler = () => { setTitle('Updated'); //state updating function }
-
Each time a component is registerd / instantiated it will create a new copy of use state, managed independantly. Even if the component exists more than once.
-
React keeps track of used state. (Will protect that var if unaffected)
-
These states ofcourse can be updated in a variety of ways. Over HTTP for example
- It will usually make sense to separate form and logic to keep things lean
- You can use standard html atts on forms as expected
- See available events some will work better than others
- We dont need a long addEventListner passing the element we just need to specify event as a param
titleChangeHandler = (event)
- event will be an object from which you can extract event.target.value
- Using this event.target.state we ensure that this data survives and is not affected by the lifecycle of the component
- Event values are always stored as string
- This is a preference You could make your code leaner by utilizing one state by passing an object
useState({t})
- Make sure you dont overwrite your state when you init your state function. So you will need to use spread operator
...userInput,
- When updating state and depend on previous you should pass in a function into state updater function. Like anonymous arrow.
setUserInput((prevState) => {return ...prevState,enteredTitle:event.target.value};)
- React schedules updates. So you could be dependant on an old snapshhot.
- Using prevState ensures everything is up to date
- You can utilize onSubmit, pass the event and assign state vars to object keys
- Dont forget to utilize preventDefault
- At somepoint you will need to clear the form data (use state)
- You can reset / clear content by setting to empty string on the submitHandler
dateChangeHandler('');
& feeding back avalue={}
att to form
- As were working with a new expense really we should be modifying our data at the same level as the rest of our expense initialization in app.js
- We can pass a function to child component and call that function inside the child
- Props can only be passd from parent->child
- For example: one level up we can pass a function
onSaveDataExpense
- Reminder: your props come in from attributes.
<ExpenseItem someProp={}>
- In the example we have passed the data and assignned it a unique id
- Just remember were passing around a pointer to a function
- First check you can pass state to the parent.
- You may want move the state between two components on the same level (two siblings for example) but at differnent branches in the component tree.
- The approach is utilizing props function to pass up app.js which the siblings share.
props.onAddExpense(expenseData)
At the moment we pass data in way of a function the state gets lifted- Infact as we have a 3 level structure it is ExpenseForm.js that passes the state up
- You only need to lift the state as far as required. Dont have to go all the way to app.js root
- Listen to change event on drop down (picked yea wants to go up to expenses)
- Forward data up
- Save in state in expense
- Pass back state prop to provide two way binding
- 2 way binding means controlling a component
- A controlled Both value and chanhgse to value are handled within the Parent Component
- A statefull component is one that manages a state
- Expense item does not have a state so its known as simply a Stateless component or Presentational Componnet
- Most pieces will only focus on outputting somthing
-
Array map() is typical tool for this creates a new array based on old ones and transforms
{props.expenses.map(expense => <ExpenseItem title={expense.title} id={expense.id} amount={expense.amount} date={expense.date}></ExpenseItem>)} ```
-
Takes argument passed as expression, executes for every element of array
-
Utilize {dynamic expression} in JSX
- Remember to update our componenet array will need to useState
- Lets create a new state
useState(INIT_STATE)
- Add new expense item first and import spread to enrich array
setExpenses([expense,...setExpenses])
- Spread works on both objects & arrays
- Alternatively we can pass function. this makes it alot cleaner
setExpenses((prevExpenses) => {return [expense,...prevExpenses]})
and returns directly
- They are required to identify
- You can inspect in the browser, if new div flashes it has been added.
- if theres no key it will actually overwrite existing element in render
- We need to add a special
key={expense.id}
prop (in most cases we will have a unique identifier anyway)
- Get the date filter to work
- Check Does select retain option
- Check date reaches Expenses.js
- Lets presume were are going to filter directly in expenses. (See what instructor propsoses)
- Create new copy of array, filter where year === `
- use filter
- should not change the overall expenses array
- derive new array or subset
- generate new key e{x} maby ? current float is strange
- special javascript evaluation
{if something true do this otherwise} && {outputsomthing}
or{filteredExpenses === 0 && <p>No Expenses</p> }
- Use ternery in jsx
- Alternatively you could simply filter the expenses before hand. Including a point to
{someContent}
- If the jsx in your component doesnt need to change you can simply return false
- Hide form
- Add new expense button shows form
- Cancel Button hides form again
- Seprate form jsx and "add new" button in NewExpense
- Return "add new button on form" when state is false
- Instructor has handled dis logic one level up in new expense
style{{}}
this wants to receive a JS object as style with keyname => value- use camel case where css attribute has
- Conditional styles not global
- Styled components
- Css Modules
- Tackling global styles
- A dynmic style may look like this:
{{color:!isValid ? 'red':'black'}}
- Beware of inline styles as they have heighest priority
${Bat ticks}
are your friend template literals- Template Literals
-
One aproach is to use styled compononets styled components
-
$ npm install --save styled-components
-
Using a library like this you will come across a tagged template literal
styled.button``;
the baticks are simply a way of calling a special method -
You can add css styling in these ticks
-
This library has all elements (html) for styling
-
can use a multiline string which is neat
-
They will create unique classnames and are global stlyles
-
Remember remove the selectors
-
Whats also nice is you can assign these styles to a whole component
<FormControl className={}>
-
In addition you can pass props to change styles dynamically
<FormControl invalid={true/false}>
border: 1px ${props => (props.invalid ? 'red' : #ccc)}
@media (min-width:x){width:auto}
can be added to your styles to detect and handle screen dynamics
- css modules
-
- Css modules require you to do your import
import styles from
- Css modules require you to do your import
-
- Include
.module
to the file name
- Include
-
- In your attribute you need to add an object instead of string
className={styles.button}
- In your attribute you need to add an object instead of string
- it will output a special class name component name_classname___unique id
- This is neat as they are globally accessible but still within in the component scope
-
- To create a dynamic property we need to use [] like this as you may get invalid property name
className={styles{styles['form-control']}}
this behaves like an array key- To continue to append classnames you can use battick method as above
@media (min-width:x){button{width:auto}}
in yourButton.module.css
- You can open up the console and inspect the js file to add a breakpoint on the line to look at the problem step by step
- You can also levarage Step into to continue on the flow
- Step over to complete the current function call
- Componenets tool will provide a tree
- Clicking on the component it will provide props view
- Keys, children, parent, the line number, hooks
- In addition you can test and change values the current state
-
AddUser Component
- does not require global styles
- create form inputs x2
- clear inputs
-
App Component
- append to array
- UI Card Component
- UI Button Component
-
[0] Managing User Input State
- validate form input
- dynamic styles for validation
- prevent default
- capture username, age inputs
- pass input state up
- pass valid state up to app.js
- isValid default to true
-
Users List Component
- Unique keys
-
Users Li item Component
-
[] Managing a list of users via state
-
[] Error Modal
- [] Dismiss
- [z] Level App.js typicaly will see modals at this level
-
Managing error state
-
Use CSS Modules
-
[] Review Code vids
-
[] Pass back specific error message to modal
-
[] Neaten Css
-
Questions:
- Where to store array of users level Users
- list of users is child li
- Need to add to app.js ?
- Generic Container stateless
- Card Container
- Where to store array of users level Users
- These are all additinal tools in the toolbelt
- You cant return / store more than one *root* element in jsx
- You cant return more than one thing in javascript either needs parent node
- Instead of JSX you can actually return an array (with a catch) *this requires a key*
- Essentially is way easier just to wrap in a `div` just beware of divistis this is actually a common issue in react. Many divs with no *semantic value*
- To get around this issue we can create a wrapper that returns nothing but
props.children
- This fullfils the requirement that one root element must be returned
- Wrapper and the jsx only has to return one thing, If you inspect you will notice we have eliminated one extra div
//Helpers/wrapper.js
const Wrapper = props => {
return props.children
} ```
//App.js
return(){
<Wrapper>
<do something>
</Wrapper>
}
```
-
<React.Fragment></React.Fragment>
or<></>
Its an empty wrapper. However it doesnt render any real html element. Gets around including another div -
You can short hand this by including
React {Fragment}
-
Portals are like fragments but help render semantically. (You wouldnt want to render a modal inside an internal div)
-
You can wrap fragments in portals.
-
Rendered Content html essential move somewhere else
-
You can use this wherever you render jsx
-
- Start by defining an element with a root say
<div id="overlay></div>"
- Start by defining an element with a root say
-
- Define a const
overlady-handler
and return your jsx for the modal itself
- Define a const
-
Import react ReactDOM
to be able to portal this to somewhere else
-
{ReactDOM.createPortal(<SomeComponent props="{}>,docuement.getElementId("overlay"))}
your implementation will look like this
- this is similar to create root in index.js
- Imagine updating a state everytime on keystroke when only required on submit. We can use refs to setup a relationship between the html element and javascript itself
const nameInputRef useRef()
assign this to a const<input ref="{nameInputRef}>
asssign attribute- Insterestingly if you now log nameInputRef you will see it has a current prop (by default undefined)
- You can now access this by using
inputRef.current.value
- You can get around reseting the state using this method. Using state in general and two extra atts !
- In the case of inputs you can just reset
inputRef.current.value = ''
(rarely to be used)
- State or refs are interchangeable.
- If just want to readonly = refs
- When accesing via refs this is called uncontrolled as were not controlling the state
- Uncontrolled input containers
-
useEffect is a tool that lets us interact with the outside world but not affect the rendering or performance of the component
-
useCallback Useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders
-
The main job of react is to Render UI & React to User Input To handle side effects.
-
Good for code to be executed in response to something
-
Side effects are everything that happens outside of normal compnenet function.
- Storage data browser
- HTTP Requests
- Managage timers
-
Many side effects should not be included in your component we do this via
useEffect()
hook -
useEffect((do after every component eval),[dependancies])
- your side affect goes into function 1 param / then specify your dependancies in function 2nd param
-
A good use of this would be authenticated user status for a login system storing auth data after refresh
-
we could use efect to handle this so client doesnt need to login again
-
localStorage.setItem('isLoggedIn','1')
-
useEffect((do after every component eval),[dependancies])
will run after component eval -
Now in storage in browser you will find this new element
-
We can now check against this variable, but we would want to control when this run
useEffect(() => { const storedUserLoggedInInformation = localStorage.getItem('isLoggedIn'); if (storedUserLoggedInInformation === '1') { setIsLoggedIn(true); } }, [setFormIsValid,enteredEmail,entererdPassword])
-
Will only run on component reload if you have no dependancies it will only run once
-
Dependancies []
-
You can pass dependacies to use effect()
[setFormIsValid,enteredEmail,entererdPassword]
-
It helps us ensure we have one code in one place which re-runs when dependancies change
-
Its also common to re-run logic when props / state change
-
useEffect accepts a function that is imperative in nature and a list of dependencies. When its dependencies change it executes the passed function. Good for timers, logging after everything has rendered
-
Sideeffects: If the component makes calculations that don't target the output value, then these calculations are named side-effects.
-
-
add everything as use effect as dependancy except:
-
Dont need to add state updating functions
-
Dont need to add "built-in" apis or functions
-
DON'T need to add variables or functions you might've defined OUTSIDE of your components
-
add all "things" you use in your effect function if those "things" could change because your component (or some parent component) re-rendered.
let myTimer; const MyComponent = (props) => { const [timerIsActive, setTimerIsActive] = useState(false); const { timerDuration } = props; // using destructuring to pull out specific props values useEffect(() => { if (!timerIsActive) { setTimerIsActive(true); myTimer = setTimeout(() => { setTimerIsActive(false); }, timerDuration); } }, [timerIsActive, timerDuration]); };```
-
-
timerIsActive
is added as a dependency because it's component state that may change when the component changes (e.g. because the state was updated) -
timerDuration
is added as a dependency because it's a prop value of that component - so it may change if a parent component changes that value (causing this MyComponent component to re-render as well) -
setTimerIsActive
is NOT added as a dependency because it's that exception: State updating functions could be added but don't have to be added since React guarantees that the functions themselves never change -
myTimer
is NOT added as a dependency because it's not a component-internal variable (i.e. not some state or a prop value) - it's defined outside of the component and changing it (no matter where) wouldn't cause the component to be re-evaluated -
setTimeout
is NOT added as a dependency because it's a built-in API (built-into the browser) - it's independent from React and your components, it doesn't change
-
Debouncing is when we would like to restrict the calling of a time-consuming function frequently.
- An example would be waiting till user has finished typing into form to perform a function.
- We could use a timer to achieve this.
setTimeout()
- In
useEffect
you canreturn
(must be function) - Return will not return until final. Imagine if this was a http you wouldnt want to send a request every milisecond
Here have assignend timeout to a variable and below placed return statement that is a function returning a clear method
const identifier = setTimeout(() => { console.log('Checking form validity!'); setFormIsValid( enteredEmail.includes('@') && enteredPassword.trim().length > 6 ); }, ; return () => { console.log('CLEANUP'); clearTimeout(identifier); }; }, [enteredEmail, enteredPassword]);
- useEffect() is the most important react hook
-
Very similar to
useState()
but more options -
Good for managing complex states
-
Returns array of two items like use effect
-
const [state,dispatchFunction] = useReducer(reducerFn, intialState, initialFm);
-
snapshot state
-
function that can be used to dispatch action
-
reducer is a function triggerd automatically recieves latest state snapshot
-
initial state
-
a function to set the initial state
-
like a state object with multiple dimensions
action.val
-
Handy for creating small pools of isolated data contexts
-
In conjunctoin Context API
-
This might make managing the state easier by separating logic (validation outside component) by grouping logic together
- You can couple this with use effect to only run when the user has finished typing
-
Nested Properties:
- use destructing to extract the valid state
- pass specific properties instead of the entire object
- Apply use affect only on the single targetted property of state
-
useReducer vs useState:
- you will know when to use reducer (when state becomes cumbersome)
- useState:
- is the main managment tool
- often unique good for independant states of data
- if state updates are easy and only a few kinds
- useReducer:
- Great if you need more power
- shold be considered if you have related pieces of state
- can be helpful if you have more complex states
- not good if you have a simple state
import { useReducer } from 'react'; function reducer(state, action) { // ... } function MyComponent() { const [state, dispatch] = useReducer(reducer, { age: 42 }); // ...
-
Context api:
-
when you pass alot of data via alot of componenets using props
-
We may find state required in lots of places this can be an issue
-
Observe in MainHeader were passing props to nav however were just passing them on. (even if this intermediate component doesnt need it) Also we only need to pass what props are needed.
-
Login function may be required in a cart and a shop in different branches in component tree (we may not have direct conection)
-
Avoids prop chain
-
use camel case for context file creation
-
To use context provide it (wrapped to it) and consume it (listen)
- add wrapped compnenets allowed to use it. Works now for all children
- App.js
<AuthContext.Provider>
- Nav.js
<AuthContext.Consumer> {(ctx) => { return (<nav>)}
takes a child that is a function and as arg you will get context data - Note:
<AuthContext.Consumer> {(ctx) =>
is kind of ugly using this return statement. can be improved using useContext hook importuseContext
and assign it like soconst ctx = useContext(AuthContext)
- We can infact pass a function as a context
,onLogout:logoutHandler}
we can no access this context value on the buttononClick={ctx.onLogout}
- props are setup to make components reuseable.
- However if we want to do somthing very specific useContext (allows code more concise)
- App.js
- add wrapped compnenets allowed to use it. Works now for all children
-
Its good to define a default for context for clarity and code completion
onLogout: () => {},
-
We can even provide a custom context provider. Putting our logout handler in here!
-
Note the structure we have now done all our login logic in an external context provider
- this will allow for separation of concerns
-
Context Limitations:
- Props for configuration
- Context state management accross components
- Context isnt made for high frequent changes (use redux)
-
Rules of Hooks:
- Only call in react functions (components & custom hooks)
- ONly call at the top level (not nested or block statements)
- Always add everything your refer to inside of useEffect as a dependancy
-
Forward Refs:
- Imperative aproach calling somthing within a function
- useImperativeHandle can be used as an alternative to state and props
- Interestingly you can pass refs as an arguemnt of a component
(props,ref)
- this needs to be coupled with a wrapper called
React.forwardRef
as in this is capabale of being bound to a ref - however you will only be allowed to access whats exposed from
useImperativeHandler()
- this needs to be coupled with a wrapper called
-
-
-
Note: instructor has used use effect to only run after state update
- Essentially react doesnt care about the web it will work with any node.
- React Dom has an interface to the web (Virtual Dom)
- Context (component wide data), Props (data from parent components), State (internal data)
- whenever these change this will update the components. react then interacts with the ReactDom
- React first defines default state, then target state- Then passes on only the differences to make this change possible to the real dom
- whenever these change this will update the components. react then interacts with the ReactDom
- Re-Evaluationg is not the same as re-rendering. Imagin if there are no changes to the dom this is just a re-evaluation
- Remember performance wise reaching out to the real dom is performance intensive process
- we can use state to make changes to dom.
- everything will come down to a state being changed. Context and Props
- for every state change of each component. react dom is re-activated this is only
- the difference between two snapshots
- You can check for flashes in element chrome console
- update mechanisim is based on comparing differences
- To a child as were initializing the component it will be re-evaluated
<Demo clickHandler={false}/>
will still run in child component. This false value is a primitative that gets re-initialez- We can specify the reloading of a component based on certain behaviour using .memo
- For functional components
export default React.memo(DemoOutput)
- For functional components
React.memo
essentially can put a filter to listen in for state changes in the case that<Button onSomthing={nochange}>
in parent looks like this where no state relative to the child changes- This is not always worth it. As memo still needs to generate new props and re-render. Good for larger apps. To cutt off an entire branch of components
- Rememver to do this evaluation it will be tricky when using ref types, Objects, Arrays etc. They are more difficult to evaluate to truthy in a comparison
- Refs and Primative
- Passing (ref types) functions may be complicated.
- We can specify the reloading of a component based on certain behaviour using .memo
- hook to store a function across component execution
undefined
let obj2 = {}
undefined
obj1 === obj2
false
obj2 = obj1 // pointed to the obj1 place in memory
{}
-
Memoization is an optimization technique for accelerating computer programs by caching the results of heavy function
-
Essentially this is the same as
useCallback
-
toggleParagraphHandler = useCallback(( )
knowing this method will never change, we can now store it for re-use
const toggleParagraphHandler = useCallback(( ) => {
setShowParagraph ((prevShowParagraph) => !prevShowParagraph)
},[dependaciesHere])
- Again by using
useCallback
we are passing the exact object (in memory) toReact.memo()
for comparison - Depancies like functiions are closures when a function is defined js locks in all vars outside. js closes over and stores them at the time of closure.
- passing in the depndacy this will allow for rerunning of the enclosed function
- Its important to understand closure and primative / ref types in JavaScript
- Two functions are objects and ARE never equal in javascript
- Closures
- Use state is ran once and handed of to be observed.
- For reevaluations no new state is created. react will only do statemanagemnt when needed.
- Child components may be reinitialed if removed from the dom. Then state will be re-initialized
- React doesnt act immediately it schedules state changes. It might feel instant ;)
- multiple updates can occur at teh same time.
- Recommended: a good way to consider is using the function method
setShowParagraph((oreShowParagraph) => !prevshowParagraph)
This ensures that is looks for the latest state change. not component re-evaluation - Procedurally a below func will not be executed the state schedule will happen first
- Updates in functions will work in batches. State Batches functions grouped together in order then in order fed to the dom in one go
-
imagine we have like a sort function but thats very intensive. This function can be memoized with sort as return.
-
Ofcourse we need to pass dependancies
-
levarage destructuring to pass just items as depndancy
const {items} = props;
-
now use memo will only run when "items" changes
const {items} = props; // destructuring // memoiszed const sortedList = useMemo(() => { return props.items.sort((a, b) => a - b); },[items])
-
dont forget you will neeed to avoid problems with ref type comparisons so wrap you props sent in
useMemo
also. -
this presumes in this scenario that the sort example function is a highly intensive process
- Classes are just anothe way to handle react components.
- This was the old react requirement (Pior 16.8)
- Instead of
return
we userender()
- Helps with error boundries
- Class based cant use react hooks
- use them for error boundries or if team is using classed based
-
- import
component
- import
-
extend
component
-
- You can now use
this.
- You can now use
- Methods you dont need to define type
const
- Old react required classes for state
- State is always an object in classes
- Must init via constructor
- Must be called state
- In your method you can overwrite old state:
this.setState({showUsers}) ;
Classes wont merge state for you - You can actually define constants in
render()
- Beware the
this
method can be tricky. You need to ensure thatthis
referes to existing class usingbind()
method - When adding constructur and extending classes you need to use
super()
- You can use lifecycle methods like:
componentDidMount()
on mounted will calluseEffect(...,[])
componentDidUpdate()
called when component is updateduseEffect(...,[somedepenadncy])
,componentWillMount()
essentially the clean up function called when unmounteduseEffect(() => {return () => {}.[]})
- Work with
componentDidUpdate()
recieving two state paramsprevProps
andprevState
// see how beautiful useEffect is in functional components heres classbased alternative
componentDidUpdate(prevProps, prevState) {
if (prevState.searchTerm !== this.state.searchTerm) {
this.setState({ filteredUsers: DUMMY_USERS.filter((user) => user.name.includes(this.state.searchTerm)) })
}
}
- Imagine getting a bunch of users from a database. You wouldnt want to get them one by one. You can use
componentDidMount()
(load when component has mounted) and send http requests in here. - Dont forget componentDidMount will only run once
useEffect
will always execute when component is intially mounted- This is a differnt mental model to follow
-
- Context consumer can be used in classes and functional by
importing
and using<Context>
You can only use one context
- Context consumer can be used in classes and functional by
-
static contextType = UsersContext
you can only do this once
- Error boundries usually handle things out of your control. Server failures etc
throw new Error('Server error)
this is a useful to provde feedback- Try catch can be tricky using components. Like try catch in a jsx.
- For this we need use errorBoundries
componentDidCatch(err)
this can be created in a separate class. This could be wrapped around a component that needs to be protected. And pass props - The dev server will provide full debug info but in production it will just show the error.
- In general these boundaries are to ensure that you can handle issues in an elegant way. Without crashing your entire application
- Browser Side apps (React) does not talk directly to db
- bad practice as you would expose creds to users in browser
- Backend can be nodejs app, PHP like api
- Tutorials http requests
-
Axios is good for handling request
-
Fetch API built in javascript
- It can be as simple as a
fetch(url)
url and return extra option in callback- Here your can target your response body.
response.success
- Here your can target your response body.
- You need to handle
fetches
response
- It can be as simple as a
-
http is aysnch it doesnt happen imediatly
-
Json Data is the most usefull
-
After making the call we can handle json and add api body call to the state
-
Promise is returned by
fetch()
-
When dealing with promises you can create the
then()
chain or useasynch
on your method andawait
. It does the same simply easier to read.fetch('https://swapi.py4e.com/api/films/').then( (response) => { return response.json() }).then((data) => { const transformedMovies = data.results.map(movieData => { return { id: movieData.episode_id, title: movieData.title, openingText: movieData.opening_crawl, releaseDate: movieData.release_date } }) setMovies(transformedMovies) })
-
With await
const response = await fetch('https://swapi.py4e.com/api/films/'); const data = await response.json(); const transformedMovies = data.results.map(movieData => { return { id: movieData.episode_id, title: movieData.title, openingText: movieData.opening_crawl, releaseDate: movieData.release_date } }) setMovies(transformedMovies)
-
Its likely you will want to use a spinner or some animation when loading
-
- Simple as defining is loaded state
-
- inside your fetch handler init this to true or false before and after.
-
- Finally wiht condition in your return
-
Loading isnt the only state we could.. "Finding more.." You always want to inform the user of the state of the application
- Technical or Internal issues can be both
- Use try catch when working with asynch await
- Fectch api will not generate an error
- See implementation Error Handler
- You dont have to perform all these inline in the return ofcourse
- Note we have alot of side effects. These can and should go into
useEffect()
- useEffect is great to handle stuff on loading cycle will change on depenacies []
- As we dont have any state dependant changes for useEffect we'll implement a
useCallback(async()
- note how you can pass
async()
followed by any dependancies
const fetchMoviesHandler = useCallback(async () => {
//Loading
setIsLoading(true);
setError(null);
try {
// With await
const response = await fetch('https://swapi.py4e.com/api/films/');
//statusCode !== 200
if (!response.ok) {
throw new Error("Status code: " + response.statusCode)
}
const data = await response.json();
const transformedMovies = data.results.map(movieData => {
return {
id: movieData.episode_id,
title: movieData.title,
openingText: movieData.opening_crawl,
releaseDate: movieData.release_date
}
})
setMovies(transformedMovies)
} catch (error) {
setError(error.message)
}
setIsLoading(false) // regardless we need to stop loading
},[])
- Sending post requests can be done by using Firebase
- Setup Firebase and create a new project and db in test mode
- By copying the link to db you can send posts
- With a final node of
movies.json
it will create a new collection for you. - You can use fetch and pass additional parameters post and decode response
- Javascript utility
JSON.stringify
async function addMovieHandler(movie) {
const response = await
fetch('https://react-http-89102-default-rtdb.europe-west1.firebasedatabase.app/movies.json',
{
method: 'POST',
body: JSON.stringify,
headers: { 'Content-Type': 'application/json' }
}
);
- just regular functions that contain statefull logic
- reuseable and in can be used with other hooks
- put your own logic together
- Remember: you can only call state functions top level
- Typically used to limit duplicated code
- use"" naming is a requirement
- same syntax as a normal function (its just a function)
-
Remember you need to tie a state to your hook exported from your custom hook. Will get imported to caller. Think of it as the body.
-
To make this state available you can return it from your hook
-
With custom hooks you are “stepping outside React” to work with some external system
-
Look how lean:
const ForwardCounter = () => {
const counter = useCounter();
return <Card>{counter}</Card>;
};
- parameters can make them configurable
- Setting a default param:
(forwards = true)
- The example uses a bunch of functions we could refactor for simplicty. The logic is dependant on other react hooks and state. For this case we need a customHook
- export function as custom hook
- but maintain state to handle in your render component
- i would guess that the instructor expects a component to just handle the request ?
- conditional dis should be handled in component ?
-
useCallback: passing a callback to its child component to prevent the rendering of the child component.
-
useMemo: It recalculated the value only when one of its dependencies change
- When envoking a callback browsers dont know how to address paramaters
- Binding is the same as a call back function and passing a parameter at the same time
function greet(name) { alert('Hi ' + name); }
- consider this function the browser doesnt know which param should be sent
- for this we can pass in a callback function
someButton.addEventListener('click', function() { greet('Max'); // yields 'Hi Max' });
- or use bind
const ForwardCounter = () => {
const counter = useCounter();
return <Card>{counter}</Card>;
};
- Revise: the where and the whys of useEffect, binding & destructuring
preventDefault()
will halt http requestuseRef
is usefull to track inputsref={nameInputRef}
- Refs have a
.current.value
property - Updating a variable everytime your component runs is
nameInputRef.current.value
is bad. Dont manipulate the DOM
- Refs have a
- Using state will give you more granualar change combined with (keystroke for example)
- Validate both server and client side
- Typically you want to check inputs are not empty
if not passed just
return
in your validation method will halt execution
- You can init state for validation to
true
to avoid inmediate outputting error messages (this is cheating) - You can levarage css classes to highlght form inputs etc
- To get around this false initalization we could creat an additonal state touched so we can check if the user has touched the input.
onBlur
The best scenario is to actually inform the user before submitting that there is an error AFTER they have had a chance at editing- When input loses focus we can initialize a function
onBlur={setNameTouched}
- You can run validation in these onBlurs in these handlers also
- After input is valid an prior to submit we would also want to remove any error message and provide instant feedback. Keystroke validation
- Up to now we have rich level of events to make for a great user experience:
changeHandler, isValid, blurHandler
valid and touched states are the most important - This is a good point to refactor: enteredValidName for example can be simplified to just a const and we can then remove the state code
const enteredValidName = enteredName.trim() !=== ''
- we can also trim things further up by combining isValid and touched states
- Finally empty your states.
setEnteredName()
andisTouched()
if necessary
- One input isnt enough to validate the whole form.
- Form is valid can be considererd a way to allow for final submission.
- Optionally you can disable submissions and buttons
- Add second input to form
- copy div block and do for email get input, validate
- Validate form group only when all inputs are valid
- Simple Input
- How could we improve this
- We could create a input component passing props and state around to nail the overall form validity is inevitabale.
- This is a case for a Custom Hook
- use-input: make inputs generric.
- we can pass our
changeHandler, blurHandler
as functions - deconstruct in your component
- call
useInput(value => value.trim() != '')
passed value to be executed inside function - return these
return {value:enteredValue,isValid:valueIsValid,valueChangeHandler
in custom hook to expose to to the calling component - in the return object we can also group togehter our resets
Todo:
- set states (isChanged, blurHandler,isValid)
- onBlur / touched logic
- isForm valid
- isTouched
- form submit handler
- if !email && !name form is ivalid.
- handle error classes
- feedback messages
- Reset inputs
- if email && name form is valid. Boolean pattern
With Reducer Form Custom Hook Component
- Redux is an alternative to context
- Is a flux-like state mangament
- Local State
- State for single cmp
- EG: Listening to user input field
- Should be mangaged in cmp with
useState
/useReducer
- Should be mangaged in cmp with
- EG: Listening to user input field
- State for single cmp
- Cross Component state
- State that affects multiple components
- EG: Open Close of a modal
- Prop Chains/Drilling
- EG: Open Close of a modal
- State that affects multiple components
- App wide state
- Affects the entire app (all / most of cmps)
- EG: User Auth
- Requires Prop Drilling
- EG: User Auth
- Affects the entire app (all / most of cmps)
- Redux gets around the problem posed by react of lots of nested context items. Or having one huge context provider
- Good for large enterprise
- Context is not optimized for High Frequency changes
- React themselves say that context its not good for High Frequency Changes
- Only have one store (central data)
- Dont directly need to manage this
- We get data out of the store by having components that have a subscription
- We have a generic reducer function that can manipulate this central store
- Components dispatch Actions are like triggers. When this change happens subscribed components get the update
- When using that function in your code, you might get a deprecation warning by your IDE or when running the app.
- Recomended to use tool kit
- Redux needs node
- Doesnt need react to run
- Redux has a reducer function that takes 2 params.
- old state
- dispatched action
- Should be a pure function Same input to same output no sideEffects
- Your reducer you then pass to your store (just a pointer)
const store = redux.createStore(counterReducer)
store.getState
gets the latest snapshot- Subscribe
store.subscribe(someMethod)
- Good practice to pass a default to
state = ''
- Dispatch
store.dispatch({type:'increment'})
- Dispatch is a method that dispatches an
- action is an object that has a type
- Dispatch is a method that dispatches an
const redux = require('redux');
const counterReducer = (state = {counter:0},action) => {
if(action.type === 'increment'){
return {counter:state.counter +1}
}
if(action.type === 'decrement'){
return {counter:state.counter +1}
}
}
const store = redux.createStore(counterReducer) // default action
const counterSubscriber = () => {
const latestState = store.getState()
console.log(latestState)
}
store.subscribe(counterSubscriber)
store.dispatch({type:'increment'})
- Normally we'd init a project like so:
npm install redux react-redux
- Create store
- Actions: increment and decrement
- invove the createStore method in file below and export that!
[store/index]
const store = createStore(counterReducer) // default action export default store
- Typically store is implemented in highest level. index.js
- Import provider
- Redux doesnt know where to read from so import the file [store/store]
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux'
import store from './store'
import './index.css';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Provider store=""><App /></Provider>);
- In your functional compoinent we need to now import more hooks.
useSelector
oruseStore, connect
- connect can be used as a wrapper
useSelector
will automatically setup a subscription for the component!!
- To dispatch an action we can utilize
useDispatch()
which will generate a function for use
- Hooks remember are not useable in components
- You can use the
connect
(h.o.c)- import it then export it like so
export default connect()(Counter)
pass your function as a return
- import it then export it like so
- To pass in props we need to setup sepecial methods to convert state to props
const mapStateToProps = state => {
return {
counter: state.counter
};
}
const mapDispatchToProps = dispatch => {
return {
increment: () => dispatch({ type: 'increment' }),
decrement: () => dispatch({ type: 'decrement' }),
}
};
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
-
Sometimes your dispathc will want to carry extra data
-
action.something
can be recieved in your store reducer and pass a value from componentif(action.type === 'increase'){ return {counter:state.counter + action.amount} }
- Action payload is just an extra property
const increaseBy5 = () => { dispatch({ type: 'increase' , amount: 5}) }
- You can add addtional params to your redux reducer. Or better yet for readablity initial state and pass that
- Redux will replace the existing state it wont merge meaning as you return your state you must return the full object
- Typically this coult be a switch
- Never mutate the state always overwrite it by returning a new object.
- Because objs and arrays are reference objects
- (this is the orignal state your getting) can be hard to debug. UI may become out of sync
if (action.type === 'increment') {
//return state.counter ++ Dont do this (mututation)
//Return new object
return {
counter: state.counter + 1,
showCounter: state.showCounter
}
}
- unique action identifiers we need to define them properly to avoid issues.
- One option is to create a constant in your redux
const INCREMENT = TRUE
and call that in your cmp
- Handling nested data can also be challengin
- Also you may endup with a large file in redux.
- To break these down and other enhancments we can use redux-toolkit
- Redux Toolkit
npm install @reduxjs/toolkit
-
createSlice
is a good alternative to reducer- provide it a slice of global state
-
Here we can avoid the long if statements
const counterSlice = createSlice({ name: 'counter', initialState: initialCounterState, reducers: { increment(state) { state.counter++; }, decrement(state) { state.counter--; }, increase(state, action) { state.counter = state.counter + action.payload; }, toggleCounter(state) { state.showCounter = !state.showCounter; }, }, });
-
This handles the imutable issue when this toolkit library is used. Much easier :)
-
We still need to return our slice and dispatch actions to it
-
const store = createStore(counterSlice.reducer)
-
We can also use
configureStore
which now allows us to specify a reducer -
Configure store will alow you to pass a **configuration object **
-
You could pass in the whole reducer or create a map of reducers
-
or merge all your reducers into one
-
Now how can we know what to dispatch ?
-
counterActions.decrement()
Dont forget this is a function call not a pointer
counter.createSlice
now is a bunch of object keys we can access- these methods are called action creators
- Now we dont need to worry about creating these action objects on our own or naming/dupe issues
-Finally these actions can be exported
export const counterActions = counterSlice.actions;
and imported in your componentimport { counterActions } from '../store';
- In your component you now pass a Payload param
dispatch({ type: counterActions.increase(5)})
- If we have a bunch of components we would like to use Redux with we need to create multiple slices
- You only need one store
- Just map your reducers as above
- access:
const counter = useSelector(state => state.counter.counter)
type:
! can now be removed from dispatch object
- Tap into store to show auth or user profile comp in app
- In head conditionally show items or not
- Dispatch action login / logout in appropriate places
- Beware we now have a two keys in reducer mapping
- One approach is to separate each reducer in its own file and use index to bootstrap them together
- in your redux we can
export default counterSlice.reducer
and import that latercounterReducer
- in your redux we can
- Reducers must be pure, side-effect free synchronous functions
- input (old state + action) -> output (new state)
- Where Should sideeffects go ?
- Components
- Action Creators
-
react redux install toolkit and react redux
-
Define state object
-
Working cart buttons
-
Hide show cart
-
add to cart
-
Update if part of cart just increase quantity
- can add more products
-
+- controlls quantity
- Remove item
- Increase item
-
1 past 0 = no items remove from cart
-
UI slice / Cart Slice ?
- UI Handle cart are
-
Add provider to somewhere high up (index.js)
-
Use store/index.js to strap slices and export "store"
-
Use store as convention
- Never put side effect or any asynch code in the reducer
- We can put these:
- inside components (ef useEffect)
- action creators
- We can put these:
- Its possible to write your own backend serverside
- Mutating the state only with use effect
- Dont:
cart.totalQuantity = cart.totalQuantity +1
- Do :
const newTotalQuantity = cart.totalQuantity +1
- Dont:
- A Fat: Reducers, Actions or Component
- Where to put logic ?
- Synchronous side-effect free ei data transformations ?
- (prefer reducers)
- Synchronous side-effect free ei data transformations ?
- Ayscnc code or code with sideeffects ?
- Action Creators or Components
- New Use reducers
- We could now listen in for changes to the cart and
useSelector()
- This we then pass as a dependacy to
useEffect()
triggering it to run only when needed PUT
Request will overwrite data. Will just create a list of data (sends data snapshot)
- Integrate
Fetch
+useEffect
Firebase into your project - Notifications: cmp
- An alternative to side affect is the action creator
- A thunk is an action that delays an action till later
- returns a functions that eventually returns another function
await
means wait for promise- Redux is prepared for action creators that return functions
- This is an alternative to the logic we have used in cmp. its nice to keep things lean
- Lets put
fetch
in a an asynch to capture errors - Reux supporst
async
returns - Lets use a separate
useEffect
to get data when the app starts - We dont want to request data and send again when cart items is passed as dep in app.js
- We can set a parameter to define if state has changed
changed:true
- Dont send if we havent changed anything locally
- You will need to handle the eventuality if there are no items
- Check all is working
- Missing has changed for notifications
- Redux Dev tools: With toolkit installed this will work with the chrome extension
- You can time travel with redux dev tools to see previous states
git fetch
git checkout origin/master -- E:\www\react-complete-guide\package.json
- Is essentially linking pages in one
- Multiple pages in one
- Url changes define changes in application
- Good for complex interfaces
- Simply add client side code to make visible html changes
npm install react-router-dom
- React Router
- You can use
createBrowserRouter
and pass objects with apath
& anelement
- With route definition objects
import {RouterProvider, createBrowserRouter} from 'react-router-dom'
import Home from './pages/Home';
import Products from './pages/Products';
const router = createBrowserRouter([{
path:'/', element:<Home/>},
{ path:'/prodcts', element:<Products />}
, {}])
function App() {
return <Router rProvider router={router} />
}
export default App;
- Instead of passing an object you can use elements
createRoutesFromElements
- We dont want to re-load scripts and sending new requests
-
import { Link } from 'react-router-dom';
-
<Link to="/products">
- Listens to events and prevents default creating new http
- We can create a layout component containing some main navigation and include it in our app
- We can use a wrapping layout component to wrap all out routes together
- You can add a parent
root
to wrap child routes (like menus) - This layout cmp can levarage
outlet
from 'react-router-dom' _Used in parent route elements to render their child route elements. _ - This way we can have path dependant layout methods
- Vistors cannot be trusted.
- So lets create a default fall back page if the route doesnt exist
- add an
errorElement
to yourrouteDefinitionObject
- add an
- React router dom is our friend
- We can also use
NavLink
and useisActive
className={(isActive) => }
- It by default checks if path is the same ;) end
prop helps handle the end of the route to handle active feedback.
useNavigate
incase you need to refresh the page programatically.
- What if these had different paths /product1 , /product2 to one cmp we wouldnt want to add all of these hardcoded
- One approach is to enter a more paths
- We could add a route then some links ,
- Good for defining different data we want to render same cmp.
- We can levarage path params by adding a colon
'/product/:productId'
import {useParams}
in your cmpparams.productId
- We can add
to
attribute to our<Link>
passing dynamic value
- Within our
createBrowserRouter
we change our path handling. You can choose either / rel or abs- remember / forward slash means absolute path
- specify our parent
/root
and - remove
/
for children to allow for creating a base url. - We can now visit
localhost:roots/products
<Link>
also has another proprelative='path||route'
- We can simply do in our route defintion object
/
orindex:true
- Backend Front End Project
- You will need to open both separatly and keep both host running
- setup route, links etc see docs
- Rendering lots of request before loading the pages is not ideal.
- It can be better to tell router to call this
- Helps with fetching data and handling different states
- loader executes before the route is rendered
- loader can be used with async to get around useing then
- aysnc await always returns a promise
useLoaderData
- You cant use loader in an above component
- Refactor and test app with loader
- These loaders can be quite heavy. So a common recomendation
is to put loader in the component where you want to use it. and export it as say
somethingLoader
- later import into your app
{loader as eventsLoader}
then use that in your route definition object loader
- We can use
useNavigation
to detext what state the transition of data is.- idle
- loading
- submitting
- You can tap into this in your jsx
{navigation.state ==='loading' && <p>Loading...</p>}
- From loader you can return a response object
- This a special
response
constructor that can be created client side - Why not just return response. With react routers response object you dont need to manually extract data from the response
- Levarage this special type of return object
- You can use any browser api in loader. APIS cookies etcw
- But you Cannot use hooks
- On way is (in response)
return { isError: true, message: 'Failed to fetch events.' };
- When you throw error reouter will return closest error element
- Error element can be use to show any error in route related code
errorElement: <ErrorPage />,
throw new Response(JSON.stringify({ message: 'Failed to fetch events.',status:response.status}))
- You can catch these errors
const error = useRouteError();
- React router has utilities to handle responses in
json()
- You can type less code and you dont need to later parse. The router can handle this for you
- After defining route you can pass another loader
- loader has two params
request
/params
- You cant use hooks in loader so you need these params to get say the
id
param in url
- We can share loaders between routes by adding it to a wrapper route or parent element
- Route definistins can have specific id's
which we use in conjuntcion with
useRouteloaderData
- We can use actions to send data
- We can use
action:
as a param to send in router - Again in actions we have access to browser api stuff but no hooks
-You will need to use the
Form
component. It will take this data and send it to your action It will omit the browser default (sending to backend) - You will need to define your method
- You can tap into
request.formData()
- or use
data.get('title')
- Note you can define actions on your form. The beauty of
<Form action='some-action'
is you can submit your form to external or different form acitons. - We can then use our
useSubmit
hook to capture this submission- It will take a form data obj param
- We can levarage
useNavigation
to extract eg all the data submitted and status of the transition to provide feedback to the user.const isSubmitting = navigation.state === 'submitting';
- You can do this in actions by returning the response first
useActionData
is our friend here to pick up the response for use in our form- Then later in your from your can acces
data.errors
- Notice we now can levarge our existing
newEventAction
as we only want to change a few things to implement an edit - We could create two events like
post
andpatch
- By changing the request method we can use the same action changing the
request.method
andurl
- A convention is to define request method in uppercase
PATCH
- Newlsetter cmp is included everywhere which poses the problem of accessing its action app wide
- We can utilize
fetcher.form
which will do the same except it doesnt init route transition - good when dont want to actuall navigate to the page to which the loader belongs
- using this you can submit form without transitioning
<fetcher.Form method="post" className={classes.newsletter}>
- you can acces the data easily:
fetcher.data
and levarageuseEffect
- Meaning you can delay loading of a script to the end.
- What if you wanted to show parts of the page before the http response has been loaded
- We can avoid await in our loader and use
defer
to which we can bundle all our http requests loadEvents()
can now be called inside our loader- if there was no promise = theres nothing to defer
- In addition we an import
await
<Await resolve={events}>
will track what has changed this will be called by router- this needs to be wrapped in a
Suspense
which works as a fallback while events are being fetched - Suspense is imported from
react
- in our loader we can now pass multiple loader actions
- inside return we can now use
Await
twice and we can define a fallback propfallback={<p>Loading...</p>}
- You can insure loader waits by using
await loadEvent)()
this is like a lever to tell react to wait for this to finish before rendering
- Avoid faking sending auth
-
Serverside Sessions:
-
Store auth on server mapped to client. CLient send identifier along with requests
-
Require tight coupling between client and server
-
Authentication token:
-
After user is authed we create but dont store a permission token
-
Client sends token along with request to resources
-
JSW json web token
-
Flow
-
Request- > Server -> Response (token) -> Client (browser)
-
Full stack apps typically dont have coupling to server / client
-
React apps are typically decoupled from server
- Have way of getting to auth page in router
- We can utilize
<Link to="?mode=${mode}>
useSearchParams
will give us access to query paramsconst isLogin = searchParams.get('mode') === 'login';
- This is a case for router actions
- Here we can levarage
const data = useActionData();
- We can then use
data.errors
to access and validate accordingly
- We can then use
- Store token just before redirect
- We have lots of ways of storing the token
- We can use all browser features
localStorage.setItem('token', token);
- Payload Token
method: request.method, headers:{ 'Authorization': 'Bearer ' + localStorage.getItem('token') }
- You can then see this token in devtools -> application
- For organization we can create a cmp for storing this in
utils
folder
- ? Missing
- We can use a path and a pass router action to clear the auth token
- no element: required
- We could use context and router to maintain this token app wide
- Or use loader to refresh tokens in the background
loader:tokenLoader,
we created earlier in utils- Within in a sub cmp we can use
useRouteLoaderData
to see it exists - Finally render your buttons conditionally
- Again we can utilize a loader
checkAuthLoader
say for example and include this on protected routes
- Logout user out after xmins and clear token
- One option is to
useEffect
in our root layour and run the checks. This component needs to be high up in the tree - We can import our
useLoaderData
to get the data to check for token - then levarage
useSubmit
hook to check if a form has been sentuseSubmit
Programatically send a form
- The timer needs to be referenced to a timestamp created in the token
const expiration = new Date(expiration.getHours() + 1); localStorage.setItem('expiration', expiration.toISOString());
- Test
- Optimize
- Side effects and memo etc are other optimization techniques
- Build For Production
- Bundled optimized, minimized code
- Upload Code to Production
- Configure server
- Loading code only when its needed
- You can levarage import as it is actually a function that returns a promise
- You can write cmp as functions. its only valid if it returns jsx
import('./pages/Blog').then(module => module.loader())
directly in your router for ex
-
- Use Special lazy function
const BlogPage = lazy(() => import('./pages/Blog'))
-
- Wrap your element in
<Suspense fallback={<p>Whoopie</p>
- Wrap your element in
You can inspect in devtools -> network
-
A react SPA application is a static website (only html, css & javascript)
- All client side
- No static service required
-
Can install something like firebase tools
- Login via ssh
- Init will connect you to a firebase project
- Ultimatley this ends with a deploy cmd
- Has a disable cmd
firebase: hosting:disable
-
GitHub actions: allows you to automate your build, test, and deployment pipeline.
- Dont forget the router needs a url to be configured
- Take it for granted users will add in strange urls
Serving the Same Build from Different Paths
- Add homepage to package.json
- Install gh-pages and add deploy to scripts in package.json
- Deploy the site by running npm run deploy
- For a project page, ensure your project's settings use gh-pages
- Optionally, configure the domain
dev: runs next dev to start Next.js in development mode. build: runs next build to build the application for production usage. start: runs next start to start a Next.js production server. lint: runs next lint to set up Next.js' built-in ESLint configuration.
- Alternative to fetch and useEffect
- where we have to manage trigger fetch
- Is a good way to perform http requests
- State, Caching and stale data handling
npm install @tanstack/react-query@beta
- Tanstack itself does NOT send HTTP requests. You have to write the code that handles this
- Tanstack then manages data,errors,caching
- useQuery function requires a promise & a key for reuse
- This key can be an array
const {data,isPending,isError,error} = useQuery({
queryKey: ['events'],
queryFn: () => fetchEvents
});
- We need to add a special provider
- Import and instantiate then wrap your app in it
- Note: if you now inspect network and close and reopen tab you will find events automatically reloads
- If we now change some data in the background tanstack will automatically update the data
- Multiple requests happen in the background to check for stale data and cached
- Throttling limiting the bandwidth available to users (dev tools show 3g)
useQuery
takes astaleTime
param- You can make sure no unecessary requests are made
gcTime
garbage cleanup
- To pass a param into useQuery can write anonymouse function
- We now need a different key for each event
- Using
useRef
will be tricky as it doesnt trigger the function to re-execute - ? only access if undefined
- An object is created on the fly and passed to useQuery
- to handle this we need to use destructuring and pass an object as param
queryFn: ({signal,queryKey}) => fetchEvents({signal,...queryKey[1]}),
anoynmous define object and pass it in
- The query as you notice will run by default (not affected by garbage or stale)
- We can disable this by passing
enabled:false
to useQuery - We can fix this by emptying object in use state and checking that this state is
!== undefined
- Mutations are a way to change data typically with a post request
useMutation
takes a configurationmutationFn: createNewEvent,
could be be doesnt have to be an anoy function- we can pass form data to mutate
const { mutate, isPending, isError, error } = useMutation({
mutationFn: createNewEvent,
});
function handleSubmit(formData) {
mutate({ event: formData });
}
- use
onSuccess
to trigger a function after mutation - We would want to do this where we fecth our data. But not dependant on page reload
- We can use
invalidateQueries
to trigger a refetch with help of theQueryClient
- this takes an {}
{queryKey: ['events']}
invalidate all queries that include this key- takes an
exact: false
also
- lets make the
queryClient = new QueryClient();
external and import it to be able to separatley trigger
- View details -> Make sure details page is loaded
- Add new [x] fetchEvent + [x] Delete to http
- fetchEvent: inside eventDetails cmp 3.5. [x] add conditional to only fetch if no data
- useParams: (you will need the id of this event, get that via useParms hook)
- Output data into events details page 4.5 [x] Get image needs title, image (url), image name,
- Add a delete button 6.5 use the delete function to delete this event trigger
- or use useMutation implement mututation to remove items - Wait till query has finished then redirect to home page
- Test search function
- [] Time format for
<time dateTime
- Just because we have invalidated the query doesnt mean we want to refetch
-
refetchType:none
- We can also use
refetchOnWindowFocus:false
to disable refetching on window focus
- Alias assingment destructuring
{mutate,isPending:isPendingDeletion }
- optimistic updating is that we update the ui before the request is
onMutate
- you will need to cancel queries first to ensure data is up to date
- At this stage the backend data will not be updated as it could be flagged as erroneous. Sp we need to
getQueryData
to get the data before the mutation onError
we can use this to rollback the data but we must use a return statement in our onMutate to make this availablecontext.previousEvent
onSettled
- when mutations is finished, refetch
- We are currently showing all events. What if we just wanted to show some
- Tweaking query to find return only some items
- Anonymous function
queryFn: ({signal}) => fetchEvents({signal,max:3}),
- Signal comes from react query and already contains this key. Its enough to reuse
- Uses spread
...queryKey[1]
- Uses spread
- Note: we are using react router in this application for routing
- Remember loaders & actions
- Load content before rendering
- We still need to keep our useQuery in our cmp as this will levarage the cache!
- Add loader to route definition object
import EditEvent, {loader as editEventLoader}
&element: <EditEvent />,loader: editEventLoader,
- Were not limited to fetching we can also mutate
- Transform to simple key data pair
Object.fromEntries(formData)
- use query helps with caching
- Is a framwork for building react
- Its a framework for production. Particularly for full stack. To enhance your react dev
- Makes it easier to integrate react features. Solves common problems.
- https://nextjs.org/
- Framworks are typically bigger than a library and have rules and strucure
- Built in server side rendering
- Will render whole dom on server.
- Good for seo and initial load
Client Side issues:
- Notice page flicker on refresh (client side rendering)
- Search engines See page content
- Routing doesn exist officially in react.
- With next we can create a folder structure and it will create routes for us
- Less code, less work and more intuitive
- Easily add your backend serverside code to your apps
- Storeing data, getting data, authenticating is easily added using react
- Create you react app
npx create-next-app
npm run build
npm run start
- Import alias is defining a rout or alias to pull impots from allowing you to simply
@components/Button
- Css modules are included by default in next
- There is no index page. This page is dynamically rendered
- Pages is the most important directory
- Api we can delete
npx create-next-app next
npm run dev
- Aside from index you can just create files in the pages directory
- You can omit
import react js
- We can levarage subfolders and indexes path segments
- urls should reflect your folder structure
- Hardcoding identifier is not ideal better to make dynamic
- Square
[newsId].js
infront of filename tells next this is a dynamic page - This also works for folders []
- Can use wrappers or imports
import {useRouter} from 'next/router'
console.log(router.query.newsId);
<li><a href="/news/newsone">First news item</a></li>
- This works but requires new request (page refresh) NOT SPA
- All our state would be lost in this scenario
- Use
<Link href="/news/newsone">First news item</Link>
import Link from 'next/link'
- This will work for seo and is a SPA
- Send meetups to backend
- fetch and dis meetups
- detail page dynamic page pull from slug (md5)
- Components work as always
- Nothing new: Pages componenets will not be loaded as pages automatically including components
- Lets import out list cmp and levarage
Link from 'next/'
- Best place to use as a layout wrapper at top level (Rather than indidual pages)
- This is the equivalent to using Link
router.push('/' + props.id);
will change url for you
- importing css modules works as expected
- Now at top level we could useEffect here with empty depenancy array to only run once and fetch data from API
- This will have two render cmp cycles.
useEffect(() => {
//send http request and fetch data async
setLoadedMeetups(DUMMY_MEETUPS)
}, []);
- Note: Will create issues for SEO as page content wont prerender this data for which we have to wait
-
Request -> Some-route -> return pre rendererd page -> hydrate react with data
-
Two Methods for prerender:
-
Static Generation (typically should use)
npm build production
- pre rendered page
-
Server Side Rendering
-
if you need to add adata to a page component you can export a function that will only work in '/pages' folder
-
export getStaticProps()
can be async -
this will run on the server and not the client
-
Always return an object. (same as props in your component)
-
Now working with props.
meetups={props.meetups}
gets around 2nd component cycle and is not available to client
npm run build
will generate static pagesnpm run start
will run the server
○ (Static) automatically rendered as static HTML (uses no initial props)
● (SSG) automatically generated as static HTML + JSON (uses getStaticProps) good for meetup id page
(ISR) incremental static regeneration (uses revalidate in getStaticProps)
-
This has threats and opportunities
- SEO is great
- Performance is great
- But data is not always up to date
-
If data does change frequently we can levarage
revalidate: 10
increamental static generation- This is the n* of seconds after which a page re-generation can occur
- Sometimes you want to rerender the page on Every Request
//runs on server after deployment
//can even add credentials here
export async function getServerSideProps(context) {
const req = context.req;
const res = context.res;
return {props:{meetups: DUMMY_MEETUPS}}
}
-
You can pass context as a param
context.req
is the requestcontext.res
is the responsecontext.params
is the paramscontext.query
is the query params
-
Sadly you will need to wait for the page to load
-
if dont need quick render and dont need access to req object. Use Props
- We can use getStatic props inside our detail cmp
- We need to get the id however to pass back to a
getStaticProps
- We can use
context.params
to get the id--const meetupId = context.params.meetupId;
- This will get you an error getStaticPath
getStaticProps
data is pregenerated. Meaning it needs to generate alll pages at build time- So we need to pregenerate all those ids ahead of time
- We can use
getStaticPaths
to do this to describe all dynamic segment values
export async function getStaticPaths() {
return{
paths: [{params: {meetupId: 'm1'}}],
}
}
- The fallback key will tell next to generate the page on the fly if it doesnt exist
fallback: true
will generate the page on the flyfallback: 'blocking'
will generate the page on the fly but will wait for the page to be generatedfallback: false
will generate all pages at build time
- We can define some paths not all if we desire. As a fallback we can use
notFound: true
to return a 404
- We can use front and backend together using routes.
- These handle http requests
- API routes are NOT about returning components
- From request we can get header, body and method.
function handler(req,res){
if(req.method === 'POST'){
const data = req.body;
//const {title, image, address, description} = data;
const {title,image,addres,description} = data;
}
}
export default handler;
- Lets used mongo db cloud db
- You will need to setup a cluster user with privladges then hit connect and get your creds.
- add your pw to connection string
- if your dbname does not exist it will be created on the fly
- collections are like tables
- documents are like rows
- dont forget to close cnx
- handle response based on status codes
- Can use fetch or axios
- We can call request to our api folder
- Next will trigger handler for us
- if whitelisting is correct etc this should work
- We can use mongo db to get data calling internal api is a bit redundant
- You can import client side or server side code
- Bundle size may vary
- The object key is not a simple integer its a an object
- Static Props are pregenerated
- to get the id for
getStaticPaths
we just need our idconst meetups = await meetupsCollection.find({}, {_id: 1}).toArray();
- we can then map these to the path objects
- We can find our data using
findOne
and will return the - to readd out objectId we need to import
ObjectId
then wrap this in the request function - expose your propss in the getProps to its component
- Its wise to check you have meta data
- We can import the
head
component from next - the simply wrap your page cmp
<Fragment> <Head>
<title>React Redux</title></Head> </Fragment>
- This can ofcourse be dynamic should you wish
- A good tool is vercel
- its made to be extremely simple
- you can use github or gitlab and deploy it
- visit personal access tokens and create a new token
- add the admin repo hook
- copy token on prompt
- vercel will run build cmd's for you
- see package json -> build "next build"
- You will need to give vercel access to your repository
- You may need to config a project furhter
- Back in mongo. Network access allow from anywhere
- We only set the right pages to generate at build time
- You can set
fallback
toblocking
- verecel will deploy on changes on push
- ** is a vite project
npm install
npm run dev
- Vite needs .jsx extension
- We can ofcourse use template literals and switch a classname
{`challenge-item-details ${isExpanded ? 'expanded':''}
- Css for animation:
transition: transform 0.2s ease-out;
This typically will be enough.
- Slide up + Fade in for example is more complex
- We can use
@keyframes
to define our animation with its name that then can be applied to our element -animation: slide-up-fade-in 0.3s;
- We can use
framer-motion
to animate elements that are removed from dom - Disapearance and Ui and complecixty can be handled by libraries
npm install framer-motion
- It has a lot of features
- Lets dive into some coding examples: '/framer-motion'
- to use motion you need to
import {motion}
- You need to use the motion component
<motion.div id="box" animate={{ x, y, rotate }} transition={{duration:3}}/>
- when you set the prop it is passed to the motion component
- It gives this natural look and feel.
- THis physics aproach feels natural
- The keys can be avoided and shortened (standard js feature)
- to use motion you need to
- To handle a close of our modal we can tap into
framer-motion
- Remeber when modifying we now dont have any state that handles (appearance/disapearance)
- Defaults inital
initial={{opacity: 0, y: -30}}
- Defaults inital
- Defaults exit
ext={{opacity: 0, y: -30}}
- Issues ofcourse can be having conditionals that remove elements from the dom
- We can use
AnimatePresence
as a wrapper to handle this-import {AnimatePresence} from 'framer-motion'
- This will run the exit animation Before removing
- We can use while to make the button
pop
const hiddenAnimationState = {opacity: 0, y: -30}
- Variants is good for definition and re-using
- can also be used to trigger states deep within a component
- framer animnation will wait for one animation to finish. Which can be problematic for delays and redundancy
- Animating step by step you can define the stagger key with params in the parent elemnent.
,backgroundColor: '#ff0080'
- visible:scale can be an array
[0.8,1.2]
- we can pass a class identifier as an argument
- Stagger can be passed as a type
- we can use ref to pass to our animate method as scopre
- Lets animate a list removal
--
--
-- Animations themselves can be retriggered saving additional code - We can do this by passing a key to the parent item
- We can create special scroll y behavious and pass them to. for the syle prop
--
--
--
--
--
--
--
--
--
--
--
--
--
--
--
- The advantages of using of context:
- is to avoid additional library
- stay just within react world
- can shrink bundle
`````
1) npm install --save react@latest react-dom@latest
2) Update index.js:
Replace
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>, document.getElementById('root'));
with
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>);
`````
- There are two approaches
- Context is its good for low *frequency updates*
- Context api is made for some states like authentication
- For hight frequency states we do have alt to redux
- In most cases redux is fine
-
Manual Testing - Preview & Test in the browser - See what the user sees - Hard to test all combinations of issues
-
Automated Testing
- Coded tests
- Test all of your apps building blocks at once
-
Unit Tests
- Individual units of code
- 12 to 100's
-
Integration Tests
- Test multiple units together
- Prove they work together
-
End-to-End (E2E) Tests
- Test complete scenarios
- Typically only contain a few
- Important but can be done manually
-
What:
- Test different things
- Unit Tests: smallest building
-
How:
- Test cucess and error cases (edge cases)
- Tools:
- Jest
- React Testing Library
- Cypress
- These are already installed in create react app
- See jest package.json
- add .text.js as convention in your
//import for testing
import App from './App';
//@param description, anonymous function
test('renders learn react link', () => {
render(<App />);
const linkElement = screen.getByText(/learn react/i); //regex
expect(linkElement).toBeInTheDocument(); //is in doc
});
npm test
will run all testsa
will run all- Tests run on watch mode (with npm test active)
- Import any needed react libs & tool
- Expect is a global function that takes a value
- Arrange, Act, Assert
- its good to have your test verbally output events.. Greeting cmp renders hello world
- the beauty of rendering inside your test is it will actually render connected components
- this is technically called an integration test
- this linkage isnt something you need to test in isolation
- Ofcourse the issue arrises when checking for text rendered by async code that changes will not happen inmediatley
findAllByRole
will return a promise. Meaning it will wait useawait
&async () =>
- We ofc would not want to send requests to a real server
- We can use
jest.mock
to mock the module - fetch is build into the browser (we dont need to test this)
- this is a very common scenario to simulate that jest has built in functions for this
- Fetch:
window.fetch.mockResolvedValueOnce({populate:MockValues});//mocking fetch
Jest Test React Lib Tests React Testing Hook
map() find() findIndex() filter() reduce() concat() slice() splice()
-
A superset of javascript
- extending javascript with these extra types
- Does not run in browser
-
Allows us to define types, avoiding potential issues like contactenating to strings together "1"+"2" instead of computing them as integers.
-
Usefull for large teams and large volumes of code.
-
This stricter typing will allow bugs to be detected (in ide) before run time.
function add(a:number,b:number)
npm init -y
(y for default)npm install typescript
- We can now use `tsc` to compile our code
- `npx tsc` will compile all files
- `npx tsc --init` will create a tsconfig.json file
- `npx tsc -w` will watch for changes
- `npx tsc with-typescript.ts`*Compile a single file for browser*
- generate *a new .js* for you
- browser friendly code
- `let age: number;` *explicitly* define a type
- Primitive types
- number
- string
- boolean
- null
- undefined
- Complex
- object
- array
let hobbies: string[]; // array type
hobbies = ['sports', 'cooking'];
// object type implicit
// let person; any type can also be implicit
let person:{
name: string;
age: number;
};
person = {
name: 'Joe',
age: 24
}
// person = {
// isEmployee:true //wrong type will throw error
// }
//Define people type but as array
let people: {
name: string;
age: number;
}[]; //store and array of objects with these properties
// we can get advanced by combining features/types
- By default typescript will try to infer as many types as possible
let course = 'React - The Complete Guide'; // infered avoids extra code
- Use inference whenever possbile
- Sometimes we may need to use multiple types
- a *Union Type*
- `let course: string|number = 'React - The Complete Guide'; //union type`
- The more ts code we write the higher the chances for repitition
- We can use type aliases to avoid this (people/person)
- Define a base type
type Person = { name: string; age: number;} let people: Person[];
- Functions + types & Generic Types
- Params can have types
- Return in a function is also a type can be implicitly set : number
function add(a: number, b: number): number { return a + b; }
- If return type so infered type is void
function print(value:any){ console.log(value);}
- Generics are a way to create reusable components
- Typesafe yet flexible
- "generic type placeholder" (T in the previous lecture)
- Work with whatever type <T> is defined as on execution `function insertAtBeggin<T>(array:T[], value:T)`
- ````
// Generics
function insertAtBeggin<T>(array:T[], value:T){ // angle brackets for generics were saying that all T's are the same type on exec
const newArray = [value, ...array]; // spread operator
return newArray;
}
const demoArray = [1,2,3]
const updatedArray = insertAtBeggin(demoArray, -1); // inserted at beggining -1 is infered as number
updatedArray[0].split(''); // error as split is not a number method
````
Generic Types ("Generics") can be tricky to wrap your head around.
But indeed, we are working with them all the time - one of the most prominent examples is an array.
Consider this example array:
`let numbers = [1, 2, 3];`
Here, the type is inferred, but if we would assign it explicitly, we could do it like this:
`let numbers: number[] = [1, 2, 3];`
number[] is the TypeScript notation for saying "this is an array of numbers".
But actually, number[] is just syntactic sugar!
The actual type is Array. ALL arrays are of the Array type.
BUT: Since an array type really only makes sense if we also describe the type of items in the array, Array actually is a generic type.
You could also write the above example liks this:
`let numbers: Array<number> = [1, 2, 3];`
Here we have the angle brackets (<>) again! But this time NOT to create our own type (as we did it in the previous lecture) but instead to tell TypeScript which actual type should be used for the "generic type placeholder" (T in the previous lecture).
Just as shown in the last lecture, TypeScript would be able to infer this as well - we rely on that when we just write:
`let numbers = [1, 2, 3];`
But if we want to explicitly set a type, we could do it like this:
`let numbers: Array<number> = [1, 2, 3];`
Of course it can be a bit annoying to write this rather long and clunky type, that's why we have this alternative (syntactic sugar) for arrays:
let numbers: number[] = [1, 2, 3];
If we take the example from the previous lecture, we could've also set the concrete type for our placeholder T explicitly:
`const stringArray = insertAtBeginning<string>(['a', 'b', 'c'], 'd');`
So we can not just use the angle brackets to define a generic type but also to USE a generic type and explicitly set the placeholder type that should be used - sometimes this is required if TypeScript is not able to infer the (correct) type. We'll see this later in this course section!
- You can google react app with typescript
npx create-react-app react-with-typescript --template typescript
- Will create ts subfolder
- Project will now consume ts and compile it to js
- Package json is mostly same + typescript and a bunch of @type dependencies
- tsconfig.json is the main config file
npm install --save @types/react-router-dom
for example- For better dev we need translation from the react/dom js libraries
- Not all libraries need translations
- We get additional ide support for props warnings and types
- After defining our type the
<Todos />
will now flag red as ts knows were missing a type as its not being used correctly
- After defining our type the
- tsconfig.js can define strictness
items:string[]
is a custom prop- FC is functional component. this will give us the correct type for props
- Pass params
<{ items: string[] }>
uses curly brackets
- Pass params
- Plugin in a concrete value to be used. As we dont want it to infer the type for props
- We need to let typescript know how to handle this prop internally
- different FCs can have different prop definitions
- This will give us nice autocompletion
- A model is where we can define what the data should look like
- We can create subfolder with the a todo.ts not tsx as its not a cmp
- Could be a class or interface
- Define properties first for TS class. As We dont need to use constructor in model. We can jump straight into the properties
- We can create this later to make sure it consumes data
- You can also use your classname as a type
React.FC<{ items: Todo[] }>
- Using these models we are defining clear structure and shape of data
- Meaning alots of errors can be solved before runtime
- Outsource todo cmp and configure correct type of cmp
- We can use a model to define the shape of our data
event: React.FormEvent
can be used to define the type of event
- We need to be implicit agaain for ts because of type definintion
- Along with this we need to define a default value (null)
const todoTextInputRef = useRef<HTMLInputElement>(null);
current?
tries to access value else store null- if you know this value is not null you can use
!
to tell ts this is not nullcurrent!
?
try to get me the value and if null store null!
Im certain that this wont be null so give me the non null value regular expressions
- if you know this value is not null you can use
- Remember we can pass pointers at functions as props to our components
- NewTodo should now be a functional component.
React.FC<{onAddTodo: (text:string) => void }>
To which we actually pass a function or pointerprops.onAddTodo?.(enteredText);
- Our type needs to be defined as anonymos:
onAddTodo: (text: string) => void;
- Remember this anonymous function expects an argument
- Our type needs to be defined as anonymos:
- We now can manage the state to which again we pass a generic type
React.useState<Todo[]>([])
- Remove todo by clicking on them
- We can use filter to remove an item from an array
- We can achieve by setting parameters with anonymous function
- onRemoveTodo: (id:string) => void }
<TodoItem key={item.id} text={item.text} onRemoveTodo={props.onRemoveTodo.bind(null,item.id)} />
- Then later using bind to pass the id
this using bind and defining
- Learn more at (Learn More)[https://typescriptlang.org]
The tsconfig.json file corresponds to the configuration of the TypeScript compiler (tsc).
These links could give you details about these attributes: (Handbook)[http://www.typescriptlang.org/docs/handbook/tsconfig-json.html] (Schema)[http://json.schemastore.org/tsconfig]
Here are some hints:
- target: the language used for the compiled output
- module: the module manager used in the compiled output. system is for SystemJS, commonjs for CommonJS.
- moduleResolution: the strategy used to resolve module declaration files (.d.ts files). With the node approach, they are loaded from the node_modules folder like a module (require('module-name'))
- sourceMap: generate or not source map files to debug directly your application TypeScript files in the browser,
- emitDecoratorMetadata: emit or not design-type metadata for decorated declarations in source,
- experimentalDecorators: enables or not experimental support for ES7 decorators,
- removeComments: remove comments or not
- noImplicitAny: allow or not the use of variables / parameters without types (implicit)
- Create React App is dead, hooks are the future utilize: "Next" "Vite" or "Remix"
- Alternatives to React App
- Practice ! apply your knowledge
- Demos and dummy projects
- re-build course projects
- come dummy businesses ideas and websites
- google for react examples
- gatsby.js
- features like static site generation, image optimization, and automatic routing, making it a comprehensive tool for web development.
- preact
- (react with smaller footprint)
- react native
- for mobile apps
- also options for desktop
- https://acad.link/nextjs
- React Native: https://acad.link/react-native
- Mern https://acad.link/mern
-
Dont stop untill you have tied up on video
-
Practice in isolation if you get stuck. Break the problem down and test in stages.
- Specifically components and props generic types in ts
- Functions asynch & promise
- Anonymous functions
- obj.? & obj.! meaning
-save @fortawesome/free-solid-svg-icons
> > npm i --save @fortawesome/free-regular-svg-icons
> > npm i --save @fortawesome/free-brands-svg-icons
npm test
npm init -y
(will answer all the default questions)
npm install redux
node redux-demo.js
execute with node
npm install redux react-redux
npm install @reduxjs/toolkit
redux tool kit you dont need redux and the tool kit // "redux": "^4.0.5",
npx create-react-app react-typescript-app --template typescript
npm install react-router-dom
npm install --save react@latest react-dom@latest
npm init -y
(y for default)
npm install typescript
npx create-react-app react-with-typescript --template typescript
npm run dev
for vite
for macOS a nd Linux
rm -rf node_modules
rm -f package-lock.json
rm -f yarn.lock
clean npm cache
npm cache clean --force
install packages
npm install
Format udemy course titles [0-999]. -> ## [0-999]min -> ##
[0-9][0-9][0-9].