-
{}
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:
<div>
Hello{" "}
{(() => {
x += 1;
return "world";
})()}
</div>
-
Components are made up of elements.
-
💡 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.
-
💡 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.
-
💡 Conceptually, components are like JavaScript functions. They accept arbitrary inputs (called “props”) and return React elements describing what should appear on the screen.
-
💡 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()
➡️render()
➡️componentDidMount()
➡️componentDidUpdate()
➡️componentWillUmount()
-
Enforcing rerender by modification to state will only be through
setState()
, hence manual changes tostate
won't have any effect. -
⭐ 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. -
💡 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 likeid
ortitle
for manual identification (any need of identification other than for render purpose, which is handled by react usingkey
) -
💡 Controlled Component: Let react handle the "single source of truth" 😃
-
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.
-
💡 Specialisation: If you want a component
A
to be some specialisation of componentB
, thenA
could just returnB
but with additionalprops
(including newchildren
). -
💡 Figure out the absolute minimal representation of the state your application needs and compute everything else you need on-demand. ➡️ 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.
- Use
React.lazy()
for lazy loading modules and wrap the component with theSuspense
component giving afallback
prop. Manage the recovery usingError Boundaries
- NOTE:
React.lazy()
NOT yet for server side rendering - NOTE:
React.lazy()
currently only supports default exports.
- NOTE:
- Web bundler (or the native browser) keeps a cache of all the resolved values of the dynamic imports promises.
- Use
React.createContext()
for "global" props (Passed down the tree at all levels, and can be overridden for indivisual subtrees).
-
[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 ofreact-error-boundary
. (Refer) (Getreact-error-boundary
from npm). Issue with usingkey
is it will force remount of the whole wrapped component in theErrorBoundary
everytime thekey
changes. -
You have to trigger
resetErrorBoundary
(more likely inFallbackComponent
) foronReset()
to work [Check bonus 7 in Epic React's React Hooks fetch API]
- 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.
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 embrace JavaScript closures ⭐ which enable side-effects without React specific APIs, custom hooks et cetera. Closure is ❤️.
-
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 forcomponentDidMount()
,componentDidUpdate()
andcomponentWillUnmount()
).- 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()
)
- Effect is run after the DOM manipulation and in async with re-painting (see how
-
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
-
⭐ 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!) -
💡 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 fromcomponentWillUnmount()
. -
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 beingprevState
which needs to return the value for new state. TODO Add why we do so!
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).
// 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
-
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.
-
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 viacloneElement
(built-in components will fail you here).
-
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).
-
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 inaction.type
.
-
Eager loading: Use dynamic imports (using
React.lazy
andReact.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 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 (orReact.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.
-
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 usingReact.useMemo
orReact.useCallback
and involves maintainance of a depedency list. Also it adds the overhead ofmemo
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.
-
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 ➡️ Change the props to accept the sliced value directly ➡️ Memoize the component ➡️ Use the hoc wrapper.
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 );
-
-
useEffect()
vsuseLayoutEffect()
Kent C Dodds's blog-
[ from React Docs ] : Unlike
componentDidMount()
orcomponentDidUpdate()
, effects scheduled withuseEffect()
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 separateuseLayoutEffect()
Hook with an API identical touseEffect()
. -
LayoutEffects are ran before browser paints the screen whereas other effects are after the painting. (Refer to donavon's React Hook flow diagram)
-
- State
- Mount / Unmount
- Hot reloading
- “Inversion of Control” : "Here you go! You have complete control over how this thing works. It’s now your responsibility."
-
Computed property names 😎
- 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?
React Router
- Using Composition to solve prop drilling
- Reduce load time - React
- Tree shake
- . . .