# Revisit React from the {Docs, Epic react, et.} (Notes for Noobs) ##### Table of Contents - [Main concepts](#main-concepts) - [Notes](#notes) - [Hooks](#hooks) - [React Patterns](#react-patterns) - [Performance](#performance) - [Click Me!](#some-useful-click-me) ## Main concepts - `{}` can have any javascript expressions. _What is a js expressions?_ Anything that resolves to a value. So we can have even IIEF using arrow functions inside `{}`. Example: ```jsx <div> Hello{" "} {(() => { x += 1; return "world"; })()} </div> ``` - Components are made up of elements. - :bulb: React elements are immutable. Once you create an element, you can’t change its children or attributes. An element is like a single frame in a movie: it represents the UI at a certain point in time. - :bulb: Thinking about how the UI should look at any given moment, rather than how to change it over time, eliminates a whole class of bugs. - :bulb: Conceptually, components are like JavaScript functions. They accept arbitrary inputs (called “props”) and return React elements describing what should appear on the screen. - :bulb: **All React components must act like pure functions with respect to their props (and state?).** - Class components should always call the base constructor with props. (`super(props)`) - **General lifecycle**: `constructor()` :arrow_right: `render()` :arrow_right: `componentDidMount()` :arrow_right: `componentDidUpdate()` :arrow_right: `componentWillUmount()` - Enforcing rerender by modification to state will only be through `setState()`, hence manual changes to `state` won't have any effect. - :star: A React Component can re-render for any of the following reasons: - Its props change - Its internal state changes - It is consuming context values which have changed - Its parent re-renders - Beware of asynchronous nature of `setState()`, hence do NOT rely on values, instead use pure functions. - :bulb: Data always flows down the tree; any state is always owned by some specific component and its effect should only be seen down the tree (sent as props). - `key` as prop is NOT accessible inside the component, use a different property like `id` or `title` for manual identification (any need of identification other than for render purpose, which is handled by react using `key`) - :bulb: **Controlled Component**: Let react handle the "single source of truth" :smiley: - Share the state by lifting the state up to the lowest common ancestor. But note that now the state is sent as prop (which is read-only), and to let the child component change the state, you might have to send a handler as prop to the child. - :bulb: Specialisation: If you want a component `A` to be some specialisation of component `B`, then `A` could just return `B` but with additional `props` (including new `children`). - :bulb: Figure out the absolute minimal representation of the state your application needs and compute everything else you need on-demand. :arrow_right: Identify which components mutates, or owns, the states. - Class components: Side effects are NOT permitted in the render function but, are permitted in hooks, life-cycle methods and event handlers. ## Notes ### Code Splitting - Use `React.lazy()` for lazy loading modules and wrap the component with the `Suspense` component giving a `fallback` prop. Manage the recovery using `Error Boundaries` - NOTE: `React.lazy()` NOT yet for server side rendering - NOTE: `React.lazy()` currently only supports default exports. - Web bundler (or the native browser) keeps a cache of all the resolved values of the dynamic imports promises. ### Context - Use `React.createContext()` for "global" props (Passed down the tree at all levels, and can be overridden for indivisual subtrees). ### Error Boundaries - [OPINION] It's better to remove the component than showing a broken UI - Take care of event handlers yourself. - Re-mounting of error boundaries can be done using `key` prop if you are using a custom ErrorBounday class component instead of `react-error-boundary`. [(Refer)](https://epicreact.dev/modules/react-hooks/useeffect-http-requests-extra-credit-solution-6) [(Get `react-error-boundary` from npm)](https://www.npmjs.com/package/react-error-boundary). Issue with using `key` is it will force remount of the whole wrapped component in the `ErrorBoundary` everytime the `key` changes. - You have to trigger `resetErrorBoundary` (more likely in `FallbackComponent`) for `onReset()` to work [_Check bonus 7 in Epic React's React Hooks fetch API_] ### Forwarding Refs - Useful when you want to pass down the `ref` to the childs and refer them from some ancestor. One good example is when we use Higher Order Components (say, a Logger Wrapper component) and we want to refer the wrapped component. ```jsx function hocWrapper(Component) { class Wrapper extends React.Component { // .. do something with life cycle methods render() { const { forwRef, ...rest } = this.props; return <Component ref={forwRef} {...rest} />; } } return React.forwardRef((props, ref) => { return <Wrapper {...props} forwRef={ref} />; }); } ``` ## Hooks - **:star: Hooks embrace JavaScript closures :star:** which enable side-effects without React specific APIs, custom hooks et cetera. Closure is :heart:. - `useState()` preserves the state between re-renders. (_Normally, variables “disappear” when the function exits but state variables are preserved by React_). - `useEffect()` adds the ability to perform **side-effects** (look it as a combined API for `componentDidMount()`, `componentDidUpdate()` and `componentWillUnmount()`). - Effect is run after the DOM manipulation and in async with re-painting (see how `useLayoutEffect()` differs) - Return a callback fn for clean ups (like `componentWillUnmount()`) - Hooks are JavaScript functions, but they impose two additional rules: - Only call Hooks at the top level. Don’t call Hooks inside loops, conditions, or nested functions. - Only call Hooks from React function components. Don’t call Hooks from regular JavaScript functions - :star: The state of each component is completely independent. Hooks are a way to **_reuse stateful logic, NOT state itself_**, all states and effects inside of it are fully isolated. In fact, since each call to a Hook has a completely isolated state, you can even use the same custom Hook twice in one component. \ This is in contrast when using custom hooks for contexts because then the state/value coming from the provider is shared between all the consumers of the provider. _(So take care of race conditions here!)_ - :bulb: Each effect “belongs” to a particular render (callback given to `useEffect()` is different each time since it's an arrow function). - Effect cleanup (_return callback from `useEffect()`_) happens after every re-render, and NOT just once during unmounting, and that's how it makes it a little different from `componentWillUnmount()`. - Use the dependency list of props/states (_second argument in `useEffect()`_) to skip effects and run only when the desired prop(s)/state(s) is changed. Also, an empty list `[]` will trigger the effect only at mount and unmount, and NOT on re-renders. - Callback can be passed to `setState()` with first arg being `prevState` which needs to return the value for new state. **TODO** Add why we do so! ```js const [state, setState] = useState({}); setState((prevState) => { // Object.assign would also work return { ...prevState, ...updatedValues }; }); ``` - Similarly, lazy computations can be done for `useState()` inital value by passing a callback which returns the initial value of the state. Also, this lazy callback will be called just once in the lifetime of the component (so no calls during re-renders). ```js // Lazy initialization const [state, setState] = useState(() => { const initialState = someExpensiveComputation(props); return initialState; }); ``` - **Batching** of `useState()` calls: If the state changes are triggered asynchronously (e.g. wrapped in a promise), they will not be batched; if they are triggered directly, they will be batched. - Usually you’ll want to declare functions needed by an effect, inside of the effect. [Link](https://reactjs.org/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies) ## React Patterns ### Context Module Functions - Making the custom hook contract such that the user of the hook, in some cases, doesn't need to care about how exactly the dispatch is called, rather just use our helper function and pass the dispatch (along with the current state and updates, if needed). \ This importable helper function is termed here as **Context Module function**. This gives user the flexibility to call dispatch by themselves for simpler operations and use the context module function for the complex ones (Looks bad for consistency:exclamation:). - A good use case is when we have multiple dispatchers, and they need to follow a certain order as the business logic. In that case, you probably don't want the user of the hook to remember the order as they will most likely miss something and it can harm the consistency of the state. - Why to export this function rather than keeping this function inside the hook or the Provider itself? Memoisation sucks, dependency list becomes unmanageable. Also, code splitting, tree shaking(:question:) and lazy loading isn't possible. ### Compound Components - `React.Children` - iterate over the children prop as list, \ `React.cloneElement` - Make a copy of the element, props are merged shallowly. - Compound Components can provide props to there children components implicitly. States could be passed down as props to the children using this pattern without lifting it off and sending it explicitly. - We can make it even more flexible by wrapping the children prop with a `Provider` and hence allowing all the nested components to access the context which contains the shared state. - **NOTE:** A DOM component is a built-in component like `<div />`, `<span />`, or `<blink />`. A composite component is a custom component like `<Toggle />` or `<App />`. We need to make sure that when we clone element, the element should actually accept a prop sent via `cloneElement` (built-in components will fail you here). ### Prop Collection and Getters - Lots of flexible/resuable components and hooks have some common use-cases(or call it the prop settings). - Prop collection gives the user of our component/hook the flexibility to re-use some default/recommended props which is exported along with our component/hook. - Prop getters are functions which take Prop collection even further by giving user the flexibility of over-riding or composing props (allowing their own logic as well as the default logic). ### State reducers - Allowing custom reducers for inversion of control. This helps the user of our component/hook to customise reduce for some or all actions (if needed). This eliminates the need of changing the original component/hook and still being able to support new feature requests at some level. - We might want to export our default reducer as well so that the user can create a custom reducer for some selected actions, and for the rest of actions, leave it to our default reducer. Also, it's a good practice to export an `actionType` object for our actions so as to eliminate the bugs due to typos in `action.type`. ## Performance - **Eager loading**: Use dynamic imports (using `React.lazy` and `React.Suspense`) and trigger the fetching as soon as the you get the hint of it being required (say hover/focus over some element). - [Webpack comments](https://webpack.js.org/api/module-methods/#magic-comments) can be helpful to defer the loading. - `import(/* webpackPrefetch: true; */ ../something)` Tells the browser that the resource is probably needed for some navigation in the future, _so fetch it as soon as you are done with the other things_. - `webpackChunkName` magic comment which will allow webpack to place common modules in the same chunk. This is good for components which you want loaded together in the same chunk (to reduce multiple requests for multiple modules which will likely be needed together) - Sometimes re-render/reconcilation is costly and since a child component can be rerendered if its parent rerenders, this can cause unnecessary re-renders and slow down the application. `React.memo` can be used to prevent these (or `React.PureComponent` for class components). Sending primitive values as props rather than doing the computation inside, can also save the pain to write custom comparators. - **Windowing**: Lazy just-in-time rendering. - Refer [react-virtual by tannerlinsley](https://github.com/tannerlinsley/react-virtual) and [react-virtualized by bvaughn](https://github.com/bvaughn/react-virtualized) - **Perf death by thousand cuts**: None of the components are slow in isolation, the problem comes when **lots** of components need to re-render when there’s a state update. - We solve the problem of our components getting a re-render triggered because of some state update in its ancestor (but wasn't required as the props didn't change) by using `React.memo`, but this usually brings a lot complexity as the props needs to be memoized either using `React.useMemo` or `React.useCallback` and involves maintainance of a depedency list. Also it adds the overhead of `memo` checking whether or not rerender is needed. - **State colocation** usually helps in solving this. The principle is _"Place code as close to where it's relevant as possible"_. If multiple components share some state, find the lowest common ancestor in the component tree and manage the state there. Same applies to Contexts, place the providers as close to the required components as possible. <img src="https://res.cloudinary.com/kentcdodds-com/image/upload/f_auto,q_auto,dpr_2.0/v1625033349/kentcdodds.com/content/blog/state-colocation-will-make-your-react-app-faster/where-to-put-state.png" width="560" height="800"> - We can also use a hoc wrapper when we know our component uses only the sliced part of the context. Abstract out the consumer out of our component :arrow_right: Change the props to accept the sliced value directly :arrow_right: Memoize the component :arrow_right: Use the hoc wrapper. ```js function withStateSlice(Comp, slice) { const MemoComp = React.memo(Comp); // Memoised Component function Wrapper(props, ref) { const state = useAppState(); // use context here const slicedState = slice(state, props); return <MemoComp ref={ref} state={slicedState} {...props} />; } // for better debugging experience, using displayName Wrapper.displayName = `withStateSlice(${Comp.displayName || Comp.name})`; // Return forwardRefed memoiszed version of sliced wrapper return React.memo(React.forwardRef(Wrapper)); } /* definition of Cell component goes here .... */ // wrap Cell with withStateSlice Cell = withStateSlice( Cell, (state, { row, column }) => state.grid[row][column] // slice function ); ``` - [Refer Recoil js](https://recoiljs.org/) ## Some useful Click Me - [`useEffect()` vs `useLayoutEffect()` Kent C Dodds's blog](https://kentcdodds.com/blog/useeffect-vs-uselayouteffect) - [ _from React Docs_ ] : Unlike `componentDidMount()` or `componentDidUpdate()`, effects scheduled with `useEffect()` don’t block the browser from updating the screen. This makes your app feel more responsive. The majority of effects don’t need to happen synchronously. In the uncommon cases where they do (such as measuring the layout), there is a separate `useLayoutEffect()` Hook with an API identical to `useEffect()`. - LayoutEffects are ran before browser paints the screen whereas other effects are after the painting. (_Refer to donavon's React Hook flow diagram_) <img src="https://raw.githubusercontent.com/donavon/hook-flow/master/hook-flow.png" alt="React Hook Flow Diagram" width="500" height="650"/> - [`useRef()` vs `createRef()` StackOverflow](https://stackoverflow.com/questions/54620698/whats-the-difference-between-useref-and-createref) - [`useEffect()` and Aborting HTTPS requests](https://academind.com/tutorials/useeffect-abort-http-requests/) - :star: [In-depth understanding of re-renders](https://felixgerschau.com/react-rerender-components/) - [How to use react hooks to fetch data?](https://www.robinwieruch.de/react-hooks-fetch-data) - [React state batching](https://medium.com/swlh/react-state-batch-update-b1b61bd28cd2) - [`useMemo()` vs `useCallback()`](https://kentcdodds.com/blog/usememo-and-usecallback/) - [Use `React.memo` wisely](https://dmitripavlutin.com/use-react-memo-wisely/) - [Call react hooks inside condition?](https://goodguydaniel.com/blog/call-react-hooks-inside-condition) - [State Colocation](https://kentcdodds.com/blog/state-colocation-will-make-your-react-app-faster) - [Fix the slow render before you fix the re-render](https://kentcdodds.com/blog/fix-the-slow-render-before-you-fix-the-re-render) - [Re-mounting vs Re-rendering](https://dev.to/tiagof/react-re-mounting-vs-re-rendering-lnh) ### Lingo - State - [Mount / Unmount](https://reactjs.org/docs/state-and-lifecycle.html#adding-lifecycle-methods-to-a-class) - Hot reloading - **“Inversion of Control”** : "Here you go! You have complete control over how this thing works. It’s now your responsibility." ### :eyes: Read later - [JSX preventing XSS, how cool is that! :cool:](https://reactjs.org/docs/introducing-jsx.html#jsx-prevents-injection-attacks) - Computed property names :sunglasses: ## Doubts - State lifting and pureness of components: Is passing a event handler to a child as prop to manage shared state breaks the idea of pure components? ### TODO (to add) - `React Router` - [Using Composition to solve prop drilling](https://twitter.com/mjackson/status/1195495535483817984) - [Reduce load time - React](https://www.infoq.com/articles/reduce-react-load-time/) - Tree shake - . . .