1: Introduction
1.1 Basic JavaScript-rendered Hello World
- How to access an element in the DOM using
document.getElementbyId(). - How to create an element using
document.createElement(). - How to set text content of an element by using
.textContentproperty. - How to append an element as child to other element using
append()andappendChild(). - How to set attributes(for eg. id) of an element using
.setAttribute(). - How to set class of an element by using
element.classNameproperty. - Difference between
.append()andappendChild().appendaccepts Node objects and DOMStrings while.appendChildaccepts only Node objects..appenddoes not have a return value while.appendChildreturns the appended Node object..appendallows you to add multiple items while.appendChildallows only a single item.
1.2 Intro to raw React APIs
- Difference between
imperetive(how) anddeclarative(what) programming. - React module is responsible for creating React elements (kinda like document.createElement()).
- ReactDOM module responsible for rendering React elements to the DOM (kinda like rootElement.append()).
React.createElement(type, props, chidren)API.ReactDOM.createRoot()API..render()API.- ReactDOM module is specific for a platform (for eg for mobile/React-native and VE platforms).
React.createElement(type, props, children): The children can also be passed as a prop.
1.3 Using JSX
- Use of Babel in browser via unpkg and
scripttag withtext/babelas type. - Use of JSX syntax in Javascript.
- JSX is
compiledto regular js by Babel, which is a code compiler. Interpolation: Defined as insertion of something of a different nature into something else.- Spread oprator in JS.
- JS and JSX interpolation and use of curly braces.
- In JS, opening angled bracket (
<) of opening html tag, triggers switching to JSX land. - In JSX, opening curly bracket (
{) triggers switching to JS land. - In JSX, closing angled bracket (
>) of closing html tag, triggers switching to JS land. - In JS, closing curly bracket (
}) triggers switching back to JSX land. - Spreading of component props in JSX.
1.4 Creating custom components
- If we look into React devtools, there is no component created when we use a normal function to create a React component.
- If we look into React devtools, there is a component created with name of the function (which is passed as the first argument) when we use
React.createElementto create an element. InReact.createElementcall, the first argument should have (if at all) a custom component function with capitalized name. In that case, React assumes that the variable (function name) of that name is in scope, otherwise it won’t create a component. - When React renders a component with props it refers to component’s
propTypesproperty (which points to an object) of the component and passes the props to the functions in this object for validation. This object has functions to validate each prop and the function name is same as the prop. This done only in development due to performance reasons. - We can use
prop-typesmodule to validated the props instead of writing our own functions for common types. - If we are using Typescript then we don’t need prop-types package. Additional benefit of TS is that the validations are done at compile time so their is no run time performance penalty.
- Using
React.Fragmentwe can create sibling component without introducing redundant divs in the DOM. We can also use an array but then we have to specify the key. React.Fragmentsare used where we want a specific HTML structure for eg in flexbox, grid or table etc.
1.5 Styling
-
There are two ways to style the components in React.
- Inline styles with the
styleprop - Regular CSS with the
classNameprop
- Inline styles with the
-
classattribute in HTML is referred asclassNameproperty in DOM. -
React/JSX uses DOM properties instead of HTML attriibutes.
-
DOM properties are camel cased in React for eg.
background-colorisbackgroundColor. -
classNameandstylein JSX represent theDOM propertiesrather thanHTML attributes. -
When spreading the props in a component, be aware about the order as the props may get overriden.
-
When spreading the
stylesprop in the component, be careful that the styles applied last will override the previous one.
1.6 forms
-
You can attach a submit handler to a form element with the
onSubmitprop. This handler will be called with the submiteventwhich has atargetproperty. That target is a reference to the<form>DOM node which has a reference to the elements of the form which can be used to get the values out of the form. -
There are several ways to get the value of the name input:
- Via their index:
event.target.elements[0].value. - Via the elements object by their
nameoridattribute:event.target.elements.usernameInput.value.
- Via their index:
-
When we submit a form, the browser, by default sends a GET request to the same page/url by sending the query paramters extracted from the form.
-
We can disable this default behaviour by using
event.preventDefault(). -
React has synthetic events (for optimization), you can access native events using
event.nativeEventfrom React code. -
console.logof a DOM node (event.target) will simply prints the DOM node HTML. We can useconsole.dirto print the DOM object. -
The
forattribute used forlabelin HTML ishtmlForin React. After doing this clicking the label will shift the focus to the input element. -
We can get the value of form inputs either from
name,idand index from theeventobject, for eg:event.target.elements[index].valueevent.target.elements.(id|name).value
-
Sometimes you want to programmatically control form inputs. Maybe you want to set their value explicitly when the user clicks a button, or maybe you want to change what the value is as the user is typing.
-
This is why React supports
Controlled Form inputs. Generally the form inputs areuncontrolledwhich means that the browser is maintaining the state of the input by itself and we can be notified of changes and “query” for the value from the DOM node. -
A
refis an object that stays consistent between renders of a React component. It has a current property on it which can be updated to any value at any time. -
In React, when we create a
refobject usinguseRefhook and pass it viarefprop of a component, React will setcurrentproperty on the object which refers to the DOM node that is created for this component. -
React allows us to programmatically set the value prop on the input like so:
<input value={myInputValue} />. If you set the value prop this way and don’t provide the onChange handler, then React will give you a warning that the input is read only as the user won’t be able to change the value. To remove the warning you can set the input asreadOnly. -
A button element can be disabled by setting
disabledattribute/prop totrue. -
We can set a default value of form input by setting
defaultValueprop.
1.7 Rendering Arrays
-
Every React element accepts a special
keyprop you can use to help React keep track of elements between updates. If you don’t provide it when rendering a list, React can get things mixed up. The solution is to give each element a unique (to the array) key prop, and then everything will work fine. -
Important rule: Whenever you’re rendering an array of React elements, each one must have a unique key prop. If each component in the list is maintaining a state the problem will be immediately observable. -
It is bad practise to set the key to index as this is what React is doing under the hood as a workaround.
-
By assigning a proper key to list component, the highlight/selection and focus is properly handled by React.
2: React hooks
2.1 useState
- Hooks are used to store data (like state) or perform actions (or side-effects).
- Hooks adds interactivity to the components.
React.useStatereturns an array of two element. The first element is a state and second element is a setter. Whenever the state is changed using the setter, the component re-renders.
2.2 useEffect
-
useEffectis a built-in hook that allows you to run some custom codeafterReact renders (and re-renders) your component to the DOM. It accepts a callback function which React will call AFTER the DOM has been updated. It is mostly used to interact with outside world for eg. db/storage, API etc. -
useEffectis not a lifecycle hook. It’s a mechanism for synchronizingside effectswith thestate of your app. -
To store and retrieve variables in the local storage, use the API below:
window.localStorage.getItem('name')window.localStorage.setItem('name', name)window.localStorage.removeItem('name')
-
To view local storage in Chrome dev tools, go to
applicationtab and click onlocal storageon the left tab. -
useStatehook uses the initial value only during the first render of the component. In the subsequent re-renders, it just ignores the initial value passed. If the initial value is computationally evaluated we can put that into a function and pass it to useState. In that caseuseStatehook will only call that function to get the state value when the component is rendered the first time and not in subsequent re-renders. This is calledlazy state initialization. The function passed is calledlazy initializer. If we are just passing a simple expression as an initial value than it is better not to use the function as it will be more of an overhead to define a function in every render.lazy initializercan also be used foruseReducerhook. -
If we use an object in the dependency array of
useEffecthook, the useEffect will trigger for every render because React does ashallow comparison(===, Object.is) and even if the object’s properties are not modified, the object is a different object. -
Re-render of the parent component triggers the render of the child components.
-
Custom components start with a
useprefix to keep the React compiler happy. Custom hooks uses other hooks which could be built-in hooks or custom hooks. -
To serialize/deserialize objects, we use
JSON.stringifyandJSON.parse. -
Component update is triggered by state change, parent element re-render, context change.
-
useEffectreturns acleanup functionwhich is runs before every invocation ofuseEffectfunction except the first and also on component unmount. -
useEffectwithout second parameter will run after every render. We depend on everything. We sync with all states. -
useEffectwith an empty array as second argument will run only after first render. We depend on nothing. We sync with no state. -
The question is not “when does this effect run” the question is “with which state does this effect synchronize with”.
- useEffect(fn) // all state
- useEffect(fn, []) // no state
- useEffect(fn, [these, states])
-
Component mount cycle/sequence
- lazy initializers for useState/useReducer.
- Render (component function execution).
- React updates DOM.
- Run layout effects.
- Browser paints screen.
- Run effects // For ALL useEffect functions which triggerred.
- Wait for user action or trigger from outside world.
-
Component re-render/update cycle/sequence
- Render (component function execution).
- React updates DOM.
- Cleanup layout effects.
- Run layout effects.
- Browser paints screen.
- Cleanup effects. // Only for useEffect functions which triggerred
- Run effects. // Only for useEffect functions which triggerred
- Wait for user action or trigger from outside world.
-
Component unmount cycle/sequence.
- Cleanup LayoutEffects.
- Cleanup Effects. // For ALL useEffects functions
2.3 Hooks follow
2.4 Lifting state
-
Lifting the state up: Required when sharing state between sibling components. The state managment is placed in the lowest common parent of siblings. The parent passes the state and mechanism to update the state.
-
As a developer, you should try to keep the state as close as possible to a component which needs it.
2.5 useState: tic tac toe
Managed State: State that you need to explicitly manage.Derived State: State that you can calculate based on other state, managed or derived.
2.6 useRef and useEffect (DOM interaction)
useRefis used to preserve a value between renders. Setting it doesn’t cause a render unlikeuseState. Thecurrentproperty stores the value. It’s main use cases are:
- To store a reference to DOM node of the components.
- To store previous state.
2.7 useEffect: HTTP requests
-
The function in
useEffectshould not be an async function as a cleanup function is expected as return instead of a promise. An asyn function automatically returns a promise (whether you’re not returning anything at all, or explicitly returning a function) -
Automatic-batching: Introduced in React 18. React groups multiple state updates into a single re-render for better performance. -
Error boundaryshould not be used application wide but near a component where its required. When implementing your ownError boundary, you should implementgetDerivedStateFromError()method, which takeserroras argument and should returns an error. When we pass akeyprop toErrorBoundarycomponent, everytime thekeyprop changes, theErrorBoundarycomponent state is reset. It is umounted and remounted. This is a generic concept, anytime a prop chsanges, the component is re-rendered. -
ErrorBoundarycomponent fromreact-error-boundarytakesErrorFallbackonResetandresetKeysas props.onResetprop takes a function which is called when theErrorBoundaryis reset.resetKeysprop is an array of values, which if changed will cause the reset of ErrorBoundary’s internal state ans will cause an unmount and remount ofErrorBoundarycomponent. -
ErrorFallbackcomponent takesresetErrorBoundaryanderrorprops. -
resetErrorBoundaryis a function and is used to reset the internal state of theErrorBoundarycomponent without umounting and remounting theErrorBoundarycomponent.
3: Advanced React Hooks
3.1 useReducer: simple Counter
-
useReduceris used to separate the state logic from the components that make the state changes. It is mostly used when the state is an object. -
When the state is an object, the reducer function “merges” the current and the new state.
-
API: const [state, dispatcherFunc] = useReducer(reducerFunc, initialState) -
Typically, you’ll use useReducer with an object of state.
-
useReducerhelps in seperating rendering and state management logic. -
Reducer functiontakes current state and action as arguments and returns the new state. The action argument can be a simple variable, object or a function. -
Dispatch functiontakes action as an argument, which is passed as second argument to the reducer function. -
As per Redux convention,
actionhas atypeandpayloadproperties. Thereducerfunction has aswitchstatement ontype. -
lazy initialization: If you pass a third function argument touseReducer, it passes the second argument to that function and uses the return value for the initial state. This could be useful if ourinitfunction (See code below) read into localStorage or something else that we wouldn’t want happening every re-render.
function init(initialStateFromProps) {
return {
pokemon: null,
loading: false,
error: null,
};
}
// ...
const [state, dispatch] = React.useReducer(reducer, props.initialState, init);
3.2 useCallback: custom hooks
-
useCallback: Returns a new function(the one pass to useCallback) on render, if the dependency list changes, otherwise returns the previous one. -
useMemo: Returns a new value (returned value) on render, if the dependency list changes, otherwise returns the previous one. -
The entire purpose of
useCallbackis tomemoize a callbackfor use independency listsandpropsonmemoized components(viaReact.memo). The only time it’s useful to useuseCallbackis when the function you’re memoizing is used in one of those two situations. -
useLayoutEffect: The callback is called as soon as the component is mounted without waiting for the browser to paint the screen. The cleanup is also called as soon as the component is mounted without waiting for anything.
3.3 useContext: simple Counter
-
Prop drilling: When the props are passed deep down from a parent component. -
Composition in React: Prop drilling can sometimes be avoided by using composition in React. -
createContext: First argument provides a default value which React will use in the event someone callsuseContextwith your context, when no value has been provided (ie not wrapping the component in provider). It is not recommend to use a default value as it’s probably a mistake to try and use context outside a provider, -
A common mistake of context (and generally any “application” state) is to make it globally available anywhere in your application when it’s actually only needed to be available in a part of the app (like a single page). Keeping a context value scoped to the area that needs it most has improved performance and maintainability characteristics.
3.4 useLayoutEffect: auto-scrolling textarea
There are two ways to tell React to run side-effects after it renders:
useEffect: If you don’t need to interact with the DOM at all or your DOM changes are unobservable.useLayoutEffect: If you need to mutate the DOM and/or do need to perform measurements. Also, if you’re updating a value (like a ref) and you want to make sure it’s up-to-date before any other code runs, then useuseLayoutEffect.- React 18 has smoothed out the differences in the UX between
useEffectanduseLayoutEffect.
Note: React updates the DOM and then browser paints the screen. After this
useEffect callback is aclled, it doesn’t block the browser from painting.
Whereas, useEffect callback runs ust after the DOM is updated and before the
browser starts to paint. If you are making observable changes to the DOM, then
it should happen in useLayoutEffect, otherwise useEffect.
3.5 useImperativeHandle: scroll to top/bottom
forwardRef: Lets your component expose a DOM node to parent component with aref.forwardRefuse cases:- Exposing a
DOM nodeto the parent component. - Forwarding a
refthrough multiple components. - Exposing an
imperative handleinstead of a DOM node.
- Exposing a
- By exposing a
refto the DOM node inside your component, you’re making it harder to change your component’s internals later. You will typically expose DOM nodes from reusable low-level components like buttons or text inputs, but you won’t do it for application-level components like an avatar or a comment. useImperativeHandle: Allows us to restrict the functionality exposed byforwardRef.
3.6 useDebugValue: useMedia
useDebugValue: UsedONLYfor custom hooks and not for buit-in ones. It also takes a second argument which is an optional formatter function. It is used when a custom hook is used multiple times and we want to track the usage.
4: Advanced React Patterns
4.1 Context Module Functions
Context Module Functions: This pattern allows you to encapsulate a complex set of state changes into a utility function which can be tree-shaken and lazily loaded. The user (component) only need to pass dispatcher function along with state and updates to functions defined in context module. The function is defined in a seperate module where the context is created and provided.- You can set the context displayName and it’ll display that name for the
Provider and Consumer in React DevTools.
const MyContext = React.createContext(); MyContext.displayName = "MyContext";
4.2 Compound Components
-
Compound Components: This pattern enables you to provide a set of components that implicitly share state for a simple yet powerful declarative API for reusable components. -
Compound components are components that work together to form a complete UI. The classic example of this is
<select>and<option>in HTML.
3, In compound components, the implicit state is passed from parent to child using React.cloneElement and passing it via a prop.
4.3 Flexible Compound Components
- The
Flexible Compound ComponentsPattern only differs from the previous exercise in that it uses React context. You should use this version of the pattern when the components to which the props have to added are notdirect descendantsof the parent but lie deep down the tree.
4.4 Prop Collections and Getters
-
The
Prop Collectionsandprop GettersPattern allows your custom hook to support common use cases for UI elements people build with your hook. -
These patterns are used for custom hooks to pass most commonly used props as an object.
-
It is more flexible to pass getter function (prop getter) which returns an object. Prop getter is a better pattern and should be used over prop collection as it allows composition of the props.
4.5 State Reducer
-
State Reducer Pattern: Inverts control over the state management of your hook and/or component to the developer using it so they can control the state changes that happen when dispatching events. -
The state reducer allows users of component to manage what state changes are made when a state change happens.
-
The user of hook/component can pass the reducer to the reducer. There is a default reducer as well, in case the user does not pass their own reducer.
-
The module containing custom hook/component can also define action
typesto prevent use of strings by the user and hence avoiding typos and increase maintainability.
4.6 Control Props
-
Control Props pattern: Allows users to completely control state values within your component/custom hook. -
This differs from the
state reducer patternin the fact that you can not only change the state changes based on actions dispatched but you also can trigger state changes from outside the component or hook as well. -
The state reducer allows users of component to manage what state changes are made when a state change happens, but sometimes people may want to make state changes themselves. We can allow them to do this with a feature called
Control Props.