React
react-fundamentals
DOM
Interactivity
Browser
Javascript
years ago, people were generating HTML on the server and then adding JavaScript on top of that generated HTML for interactivity. However, as requirements for that interactivity became more challenging, this approach produced applications that were difficult to maintain and had performance issues.
So modern JavaScript frameworks were created to address some of the challenges by programmatically creating the DOM rather than defining it in hand-written HTML
Bare bones; making elements using DOM APIs
<html>
<body>
<div id="root"> </div>
<script type="module">
const div = document.createElement('div')
div.textContent = 'Hello World'
document.getElementById('root').append(div)
</script>
</body>
</html>
<html>
<body>
<script type="module">
const div = document.createElement('div')
div.textContent = 'Hello World'
const root = document.createElement('div')
root.setAttribute('id', 'root')
root.append(div)
document.body.appendChild(root)
</script>
</body>
</html>
create elements using js; append to root element
define root element also using js (used in portals)
why make elements using js?
- js gives us more control
for eq. div.textContent = new Date()
React
- Abstracts away browser's imperative APIs to give us declarative APIs
Working with react is 2 step process
- document.createElement()
- creates an object of type HTMLElement or Element
- React.createElement() creates an object of type React Element
- root.append()
- inserts javascript object to DOM
- ReactDOM.createRoot() inserts react elements to DOM
This 2 step process helps us target multi-platforms.
Rewrite DOM APIs using React APIs
<body>
<div id="root"></div>
<script src="https://unpkg.com/react@18.1.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18.1.0/umd/react-dom.development.js"></script>
<script type="module">
const element = React.createElement('div',
{ children: 'Hello World', className: 'container' })
const rootElement = document.getElementById('root')
const root = ReactDOM.createRoot(rootElement)
root.render(element)
</script>
</body>
<html>
<body>
<div id="root"> </div>
<script type="module">
const div = document.createElement('div')
div.textContent = 'Hello World'
const rootElement = document.getElementById('root')
rootElement.append(div)
</script>
</body>
</html>
nesting elements
<body>
<div id="root">
<div class="container">
<span>Hello</span>
<span>World</span>
</div>
</div>
</body>
const hello = React.createElement('span', { children: 'Hello' })
const world = React.createElement('span', { children: 'world' })
const element = React.createElement('div', { children: [hello, world], className: 'container' })
// both produce same ouput
const element = React.createElement("div", null, "Hello");
const element = React.createElement("div", {}, "Hello");
// both produce same ouput, but small difference(see open questions)
const element = React.createElement("div", null, ["Hello", "World"]);
const element = React.createElement("div", {
children: ["Hello", "World"],
});
my solution:
problem:
its difficult to write React APIs by hand; therefore -> JSX
- HTML like
- intutive, easy to read
const ui = <h1 id="greeting">Hey there</h1>
const ui = React.createElement('h1', {id: 'greeting', children: 'Hey there'})
JSX
React API
Babel
Tip: If you can train your brain to look at JSX and see the compiled version of that code, you’ll be MUCH more effective at reading and using it! I strongly recommend you give this some intentional practice.
JSX differences with HTML
- class -> className
Babel in action
<html>
<head>
<script>
"use strict";
var element = /*#__PURE__*/React.createElement("div", {
className: "container"
}, "Hello World");
ReactDOM.createRoot(document.getElementById('root')).render(element);
</script>
</head>
<body>
<div id="root">
<div class="container">Hello World</div>
</div>
<script src="https://unpkg.com/react@18.1.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18.1.0/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone@7.12.4/babel.js"></script>
<script type="text/babel">
const element = <div className="container">Hello World</div>
ReactDOM.createRoot(document.getElementById('root')).render(element)
</script>
</body>
</html>
<body>
<div id="root"></div>
<script src="https://unpkg.com/react@18.1.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18.1.0/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone@7.12.4/babel.js"></script>
<script type="text/babel">
const element = <div className="container">Hello World</div>
ReactDOM.createRoot(document.getElementById('root')).render(element)
</script>
</body>
Babel, steps required
<body>
<div id="root"></div>
<script src="https://unpkg.com/react@18.1.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18.1.0/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone@7.12.4/babel.js"></script>
<script type="text/babel">
const element = <div className="container">Hello World</div>
ReactDOM.createRoot(document.getElementById('root')).render(element)
</script>
</body>
1. without this babel doesn't know what to compile
<body>
<div id="root"></div>
<script src="https://unpkg.com/react@18.1.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18.1.0/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone@7.12.4/babel.js"></script>
<script type="text/babel">
const element = <div className="container">Hello World</div>
ReactDOM.createRoot(document.getElementById('root')).render(element)
</script>
</body>
2. babel knows it is react code; therefore compilation would be successful even without react imports but compiled code won't run correctly if react is not imported
Template Literal != embedding expression in JSX
const greeting = 'Sup'
const subject = 'World'
const message = `${greeting} ${subject}`
const className = "container";
const children = "hello world";
const element = <div className={container}>{children}</div>
in jsx, backtick & $ are not required!
template literal
Interpolation
the insertion of something of a different nature into something else
const str = `Hello "world"`
in js by template literal makes interpolation possible (strings delimited by ` )
const greeting = 'Sup'
const subject = 'World'
const message = `${greeting} ${subject}`
template literal allows embedding expressions (delimited by $ & { } )
const meeting = ({greeting}) => <div>{greeting}</div>
in jsx delimit by { }
expression vs statement
- expression: i*i
- returns some value
- statement: i = 1
- doesn't return value
- if(i==i){}
spread operator
const props = {
className:'abc'
}
<div className="default" {...props}>Hello</div>
<div className="abc">Hello</div>
<div {...props} className="default">Hello</div>
<div className="default">Hello</div>
- spread operator is just Object.assign
order of attribute matters!
Calling react functional component as
function vs as component
const element = (
<div>
{Message({children:'hello'})}
</div>
)
const element = (
<div className="container">
<Message>Hello</Message>
</div>
)
function Message({children}){
return <div className="message">{children}</div>
}
const element2 = /*#__PURE__*/
React.createElement(
"div",
null,
/*#__PURE__*/ React.createElement(
Message, null, "Hello"
)
);
const element = /*#__PURE__*/
React.createElement(
"div",
null,
Message({
children: "hello"
})
);
React functional component
call as function
render as component
- not passes through react render cycle, so it will be faster, but may create problems, especially if function as hooks
Called as function
Called as react component
why function component need to start with uppercase
babel transformation;
message as string at 1st place but as component in 2nd place
Guess right babel conversions
const element = (
<div className="container">
{React.createElement
(Message, {children: 'hello'})
}
</div>
)
const element2 = (
<div>
<Message greeting="Hello" subject="World" />
</div>
)
const element3 = (
<>
<Message>Hello</Message>
</>
)
const element1 = /*#__PURE__*/ React.createElement(
"div",
{
className: "container"
},
React.createElement(Message, {
children: "hello"
})
);
const element2 = /*#__PURE__*/ React.createElement(
"div",
null,
/*#__PURE__*/ React.createElement(Message, {
greeting: "Hello",
subject: "World"
})
);
const element3 = /*#__PURE__*/ React.createElement(
React.Fragment,
null,
/*#__PURE__*/ React.createElement(
Message, null, "Hello")
);
styling react components
- inline style with `style` prop
- regular css with `className` prop
thing to take care of
- styles are camelCases than kebab-cased
- backgroundColor vs background-color
- styles are combination of jsx expression & object expression
<div style={{marginTop: 20, backgroundColor: 'blue'}} />
this is because of DOM and not react.
DOM understands className but not class
DOM understands CSS as CSSStyleDeclaration object and not as string.
<div className="container" style={{fontStyle:'italic'}}>
Hello
</div>
here className and style represent DOM properties and not HTML attributes
Form
ways to get value
- event.target.elements.username.value
- event.target.elements[0].value
function UsernameForm({onSubmitUsername}) {
const handleSubmit = event => {
event.preventDefault()
const username = event.target.elements.usernameInput.value
onSubmitUsername(username)
}
return (
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="usernameInput">Username:</label>
<input id="usernameInput" type="text" />
</div>
<button type="submit">Submit</button>
</form>
)
}
Extra notes about form
- submitting a form updates url with query params
- ?username=sanyam
- event is SyntheticEvent, react does it for performance reasons
- to get actual event -> `event.nativeEvent`
can use name or id, both work same
to connect label with text (for form reader and connect by click)
- use htmlFor
- for is html attribute, htmlFor is DOM field
3. useRef
- A ref is an object that stays consistent between renders of your React component.
- It has a current property on it which can be updated to any value at any time
- inputRef.current.value
- createRef doesn't have much use in FC, more here javascript - What's the difference between `useRef` and `createRef`? - Stack Overflow
function UsernameForm({onSubmitUsername}) {
const inputRef = React.useRef()
const handleSubmit = event => {
event.preventDefault()
const username = inputRef.current.value
onSubmitUsername(username)
}
return (
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="usernameInput">Username:</label>
<input ref={inputRef} id="usernameInput" type="text" />
</div>
<button type="submit">Submit</button>
</form>
)
}
Uncontrolled vs Controlled Form
function UsernameForm() {
const [username, setUsername] = React.useState('')
const handleChange = event => {
const {value} = event.target
setUsername(value)
}
return (
<input
id="usernameInput"
onChange={handleChange}
value={username}
type="text"
/>
)
}
function UsernameForm() {
const handleChange = event => {
const { value } = event.target
// do something based on value
};
return (
<input
id="usernameInput"
onChange={handleChange}
type="text"
/>
)
}
- the browser is maintaining the state of the input by itself
- we can be notified of changes or “query” for the value from the DOM node
key prop
- what is default value of key?
- index of the element
react imports
https://unpkg.com/react@16.7.0/umd/react.production.min.js
- umd - universal module definition
- allows us to create global variable in browser, which we can use in our code
react-hooks
React.useState
const [name, setName] = React.useState('initialvalue')
getter
setter or state dispatch function
can be a value or a function.
if function, called only first time - can be used for perf optimization
const [name, setName] = React.useState(localStorage.get('name')??initialValue)
const [name, setName] = React.useState(()=>localStorage.get('name')??initialValue)
called every time component is re-rendered
React.useEffect()
Lifting State Up & Colocation
- Lifting State Up – React (reactjs.org)
- https://kentcdodds.com/blog/state-colocation-will-make-your-react-app-faster
we often lift state up, but rarely colocate; colocating brings performance benefits and we should move states closer to component.
using debounce or useMemo?
think if it can avoided just by co-locating the code.
share recommended extensions
.vscode/extensions.json
{
recommendations:["abc","def"]
}
npm trick
npm run test = npm test = npm t
npm run start = npm start
console.log vs console.dir
Open Questions
- why React.createElement('div',{children:[A,B]}) will not throw console warning but not React.createElement('div',null,[A,B])?
RxJs
Observable
- counterpart of an array
- instead of retaining data, it streams to interested subscribers
- only active when it is subscribed
react
By xerosanyam
react
- 683