React-Tutorial
React-Tutorial
What is React?
React is a free and open-source front-end JavaScript library for building user interfaces which are called
components. It is maintained by Meta and a community of individual developers and companies.
React components are displayed performantly using a virtual DOM (Document Object Model). You may be
familiar with the real DOM. It provides the structure for a web page. However, changes to the real DOM can
be costly, leading to performance problems in an interactive app. React solves this performance problem
by using an in-memory representation of the real DOM called a virtual DOM. Before React changes the
real DOM, it takes a snapshot of the virtual DOM and compares it against the original virtual DOM, and
calculates the minimum amount of changes required to the real DOM. Finally, the real DOM is updated only
for those minimum changes.
When there is a update in the virtual DOM, react compares the virtual DOM with a snapshot of the virtual
DOM taken right before the update of the virtual DOM. With the help of this comparison React figures out
which components in the UI need to be updated. This process is called diffing or Reconciliation.
Understanding JSX
JSX is a syntax that stands for JavaScript XML we use in a React component to define what the
component should display.
function App() {
return (
<div className="App">
<Alert type="information" heading="Success">
Good Day!
</Alert>
</div>
);
}
You can see that JSX looks a bit like HTML. However, it isn’t HTML because an HTML div element
doesn’t contain a className attribute, and there is no such element name as Alert. The JSX is also
embedded directly within a JavaScript function, which is a little strange because a script element
is normally used to place JavaScript inside HTML.
JSX is a JavaScript syntax extension. This means that it doesn’t execute directly in the browser – it
needs to be transpiled to JavaScript first. A popular tool that can transpile JSX is called Babel.
We have seen that React uses a className attribute rather than class for CSS class references.
This is because class is a keyword in JavaScript, and using that would cause an error.
In React, both useRef and forwardRef are hooks used to interact with and manage the DOM elements and
component references, respectively. They serve different purposes in the React ecosystem.
useRef: The useRef hook is primarily used to access and interact with DOM elements directly. It creates a
mutable object called a "ref" that persists across renders and holds a reference to a DOM element or any
other value. Unlike state or props, changing the value of a ref doesn't trigger a re-render of the component.
Example usage of useRef:
function MyComponent() {
const inputRef = useRef(null);
useEffect(() => {
// Focus the input element when the component mounts
inputRef.current.focus();
}, []);
In this example, the inputRef holds a reference to the input element, and the useEffect hook is used to
focus the input element once the component mounts.
forwardRef: The forwardRef function is used when you need to pass a ref from a parent component down
to a child component. This is useful when creating reusable components that need to expose their internal
DOM elements to parent components. Without forwardRef, a ref passed to a component would not be
available on the DOM element that the component renders, as refs are not automatically forwarded to
custom components.
function ParentComponent() {
const inputRef = useRef(null);
In this example, the forwardRef function allows the inputRef to be forwarded and used on the input element
rendered by the MyInput component in the ParentComponent.
In summary, useRef is used to create a mutable reference to DOM elements or other values that persists
across renders, while forwardRef is used to forward a ref from a parent component to a child component,
allowing access to the child's underlying DOM element.
Hooks are the new feature introduced in the React 16.8 version. It allows you to use state and other React
features without writing a class. Hooks are the functions which let us use state and lifecycle features from
function components. It does not work inside class components.
useEffect Hook
The useEffect hook is perfect for handling side effects caused by mounting, un-mounting, changing state,
etc.
useEffect(() => {
console.log('This is a side effect')
})
The above side effect will now run on every single render of the component. That means when the
component is first mounted, when the props change, and/or when the state changes. This is really nice
since code no longer needs to be duplicated between the mounting and updating life cycle methods like in
a class component.
useEffect(() => {
console.log('Only run on mount')
}, []);
The useEffect takes an optional second parameter which is an array of values. This array of values is
compared during each re-render with the previous render’s array values and the side effect will only be run
if the values in the array changed since the last render. This means if you only want to run a side effect on
mount then you can pass an empty array as the second parameter.
Having this second array parameter is really nice since it allows side effects to be run whenever any value
changes. For example if the url from our component changes we can run a side effect
useEffect(() => {
console.log('Only run on url change')
}, [url])
useEffect(() => {
console.log('This is my side effect')
return () => {
console.log('This is my clean up')
}
});
useLayoutEffect Hook
Ever wanted to get the size of a component in your React app? You might need it for a lightbox component
to know the size of an image or to have an element with an animated size change. 🤔
Good news! React has got you covered with the useLayoutEffect hook. 🎉 This hook lets you run some
code after the layout and paint have happened, making it perfect for this use case.
The hook uses the useState hook to keep track of the dimensions of a component and returns an object
with two properties: width and height. 💪
With useLayoutEffect, we handle the resize of the component when it's first mounted and when the
component is resized. 🔧
And here's a fun fact: If the browser supports the ResizeObserver API, the hook uses this API to watch the
component for size changes and update the dimensions. 🚀 If not, it falls back to using the traditional
window .addEventListener approach.
It's a modern browser API that allows you to observe changes in the size of an element. 🔍 It's more
efficient than using traditional event listeners like window. resize or window. orientationchange, especially
when you have multiple elements to observe. 💡
So, there you have it! 🎉 The useLayoutEffect hook and the ResizeObserver API are tools to help you get
the size of a component in your React app. Happy coding! 💻
useContext Hook
Context API uses Provider and Consumer Components but passing down data using Consumer
component is very hard to write the long functional code to use this Context API. So useContext hook in
functional component helps to make the code more readable, less verbose and removes the need to
introduce Consumer Component. The useContext hook is the new addition in React 16.8. For example:
The useContext provides the initial context value and then re-render the component whenever its value
changes by the Context.Provider component and we can also optimize its performance by using
memoization while providing context value. Here is an illustration of it:
useTransition Hook
useTransition is a React Hook that lets you update the state without blocking the UI. When we make a state
change, React will try to combine all of the changes into one render call and then render the application
once all of the changes have been computed. This can be a problem if one of the state changes is slow or
computationally expensive because it will delay the rendering of the entire application.
React provides us useTransition hook, which can help optimize our UI performance by deprioritizing
expensive state changes and allowing other renderings to take place first.
Here are some features of useTransition Hook:
1. Combining State Changes: React usually combines state changes into a single render call to
optimize performance. However, this can cause delays if one state change is computationally
heavy.
2. Urgent and Non-Urgent Updates: With React 18's concurrency features, you can mark state
updates as either urgent or non-urgent using transitions. This is particularly useful for scenarios
where quick updates might be delayed by heavy updates.
4. Background Processing: The useTransition hook enables you to specify that a state change
should be processed in the background, without blocking the rendering of urgent updates. React will
render the application only after the non-urgent transition has been completed.
5. Urgent Rendering: Urgent rendering refers to rendering updates that are immediately required to
keep the UI responsive and functional. When you update the state using hooks like useState, React
treats those updates as urgent and tries to render them as soon as possible. Urgent rendering
ensures that user interactions, animations, and other time-sensitive updates are given priority to
maintain a smooth user experience.
6. Non-Urgent Rendering with useTransition: When you use the useTransition hook to mark a state
change as non-urgent, you're telling React that this update can be processed in the background,
without blocking urgent rendering. React will prioritize rendering updates marked as urgent. Once all
the urgent updates are rendered, React will then process and render the non-urgent updates. This
allows the UI to remain responsive even if there are heavy or time-consuming non-urgent updates
happening in the background. The non-urgent updates do not block the UI from rendering.
startTransition(() => {
setSearch(e.target.value);
});
Since we wrapped the setSearch with startTransition, now setSearch is a non-urgent operation. That
means React will first finish the urgent updates before working on the heavy updates like setSearch.
For example:
function SearchComponent() {
const [search, setSearch] = useState('');
const [isPending, startTransition] = useTransition();
return (
<div>
<h2>Search Component</h2>
<input
type="text"
value={search}
onChange={handleSearchChange}
placeholder="Search..."
/>
<div>
{isPending ? (
<p>Loading search results...</p>
):(
<ul>
{/* Simulating search results */}
{Array.from({ length: 10 }).map((_, index) => (
<li key={index}>Result {index + 1}</li>
))}
</ul>
)}
</div>
</div>
);
}
useDebugValue is a React Hook that lets you add custom labels or values to a custom Hook in React
DevTools.
When you create a custom hook, you might want to provide more information to developers who use that
hook. This is where useDebugValue comes in. By calling useDebugValue inside your custom hook, you
can associate a label or a value with that hook, which can be seen in React DevTools when inspecting the
component that uses the hook.
Examples:
useDebugValue(isOnline ? 'Online' : 'Offline');
This lets you avoid running potentially expensive formatting logic unless the component is actually
inspected. For example, if the date is a Date value, this avoids calling toDateString() on it for every render.
The formatting function will only run when we inspect it from the React DevTools.
Example 01:
function useCounter() {
const [count, setCount] = useState(0);
Example 02:
function useCount(initialValue) {
const [count, setCount] = useState(initialValue || 0);
useEffect(() => {
const interval = setInterval(() => {
setCount(prevCount => prevCount + 1);
}, 4000);
return () => {
clearInterval(interval);
};
}, []);
useDebugValue(count);
return count;
}
Example 03:
useEffect(() => {
async function fetchData() {
try {
const response = await fetch(url);
const json = await response.json();
setResponse(json);
setLoading(false);
} catch (error) {
setError(error);
}
}
fetchData();
}, [setError, setResponse, url]);
Example 04:
useReducer Hook
The useReducer is used for complex state management. The useReducer(reducer, initialState) hook
accept 2 arguments: the reducer function and the initial state. The hook then returns an array of 2 items:
the new state and the dispatch function for example:
The useReducer hook is used for state management. React already has another hook which is useState.
So unlike the useState it is also a mechanism to change state.
For example:
function MyComponent() {
const [state, dispatch] = useReducer(reducer, initialState);
const increamentAction = {
type: ''increase''
};
const decrementAction = {
type: ''increase''
};
return (
<>
<h1>Counting Result - {state}</h1>
<button onClick={() => dispatch(increamentAction)}>
Increment by 1
</button>
<button onClick={() => dispatch(decrementAction)}>
Decrement by 1
</button>
</>
);
}
React checks whether the new state differs from the previous one. If the state has been updated, React re-
renders the component and useReducer() returns the new state value: const [newState, ...] =
useReducer(...).
useCallback Hook
The React useCallback Hook returns a memoized callback function. Think of memoization as caching a
value so that it does not need to be recalculated.
The useCallback hook is used when you have a component in which the child is rerendering again and
again without need just because it has a function prop that is passed down from its parent component.
We can fix it to wrap our function with useCallback hook and pssing an array of dependencies. Then
useCallback will return a memoized version of the callback that only changes if one of the dependencies
has changed.
1. When the function object is a dependency to other hooks, e.g. useEffect(..., [callback]).
2. When the function has some internal state, e.g. when the function is debounced or throttled.
3. A functional component wrapped inside React.memo() accepts a function object prop.
For example:
useMemo Hook
The useMemo is very similar to useCallback. It also accepts a function and a list of dependencies, but the
difference between useMemo and useCallback is that useMemo returns the memo-ized value returned by
the passed function. It also recalculates the value when one of the dependencies changes. It’s very useful if
you want to avoid expensive calculations on every render when the returned value isn’t changing.
1. You can use useMemo when you are working on functions where the inputs gradually change.
For example:
useCallback and useMemo are two important hooks in React that help optimize the performance of your
components by memoizing values and functions. They are both used to avoid unnecessary re-renders and
re-computations in your application. The React docs say that useCallback returns a memoized callback
and the useMemo returns a memoized value.
Simply, the useCallback and useMemo both expect a function and an array of dependencies. The
difference is that useCallback returns its function when the dependencies change while useMemo calls its
function and returns the result.
The APIs of useCallback and useMemo look similar. They both take in a function and an array of
dependencies.
useCallback(fn, deps);
useMemo(fn, deps);
For example:
function foo() {
return 'bar';
}
memoizedCallback;
// ƒ foo() {
// return 'bar';
// }
memoizedResult; // 'bar'
memoizedCallback(); // 'bar'
memoizedResult(); // TypeError
If you want you can also use useMemo as useCallbak since they both take JavaScript function, so
useCallback(fn, deps) is equivalent to useMemo(() => fn, deps)
useCallback:
useCallback is used to memoize functions. When you pass a function to useCallback, it returns a
memoized version of that function. This is particularly useful when you're passing functions as props to
child components.
return (
<ChildComponent onClick={handleClick} />
);
};
In the example above, handleClick will remain the same between renders as long as its dependencies (in
this case, none) remain the same. This can help prevent unnecessary re-renders of the ChildComponent.
useMemo:
useMemo is used to memoize values or computations. It takes a function that returns a value and a
dependency array. The value returned by the function will be memoized until the dependencies change.
return (
<div>
<p>Result: {expensiveCalculation}</p>
</div>
);
};
In this example, expensiveCalculation will only be recalculated if any of the dependencies in the array
change. This can be helpful for avoiding redundant calculations, especially when the calculations are
resource-intensive.
Both useCallback and useMemo help improve performance by avoiding unnecessary work, but it's
important to use them judiciously. Overusing them can lead to more complex code and potentially
negatively impact readability. Use them when you notice that functions or values are being re-calculated or
re-rendered more often than needed.
Custom Hook
A custom hook in React is a JavaScript function that utilizes one or more built-in hooks (such as useState,
useEffect, etc.) to encapsulate certain logic or behavior, allowing you to reuse that logic across multiple
components. Custom hooks are a way to create reusable pieces of stateful or side-effect logic in a modular
and organized manner.
Custom hooks follow a naming convention that starts with the word "use" to indicate that it's a hook,
followed by the specific functionality or logic it provides. For example, you might create a custom hook
named useFetch to handle data fetching using the useEffect and useState hooks.
Custom hooks allow you to extract complex logic into reusable units, making your components cleaner and
more focused on presentation. They also help in sharing logic across different components without
duplicating code which is alternative to HOC.
function useCounter(initialValue) {
const [count, setCount] = useState(initialValue);
function CounterComponent() {
const { count, increment, decrement } = useCounter(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
}
In the example above, the useCounter custom hook encapsulates the state management and behavior
related to the counter. It returns an object with the current count value, as well as increment and decrement
functions. The CounterComponent then uses this custom hook to manage its state and behavior.
Higher Order Component - HOC
Higher Order Component (HOC) is a design pattern where a function takes one component and returns
another component with enhanced functionality. In other words, it's a component that wraps another
component to provide additional props, behavior, or modifications to the original component. HOCs are a
pattern used to reuse component logic and apply cross-cutting concerns like data fetching, authentication,
authorization, or conditional rendering.
Code Reusability: HOCs allow you to extract common logic from multiple components and reuse it across
your application.
Cross-Cutting Concerns: HOCs are useful for implementing cross-cutting concerns such as
authentication, authorization, logging, and data fetching. These concerns often need to be applied to
multiple components, and HOCs make it easier to centralize the logic.
Conditional Rendering: HOCs can enable or disable rendering of a component based on certain
conditions, allowing you to implement features like conditional access control.
This new component is enhanced because it’s going to get shared reusable functionalities as props from
the HOC. The Higher-Order Components (HOC) stem from the concept of Higher-Order Functions (HOF)
which is called this way whenever it takes a function as argument or returns a function with its return
statement.
Function Curring/HOF:
const multiply = (multiplier) => (multiplicand) =>
multiplicand * multiplier;
const TODOS = [
{ id: '1', task: 'Do this', completed: true },
{ id: '2', task: 'Do that', completed: false },
];
function App() {
const { data, isLoading } = fetchData();
A blueprint for a Higher-Order Component that is just taking a component as input and returning the same
(read: none enhanced) component as output looks always as follows in actual code:
// withHigherOrderComponent.tsx
function withHigherOrderComponent(Component: any) {
return function (props: any) {
return <Component {...props} style={{ color: "green", height: "200px" }} />;
};
}
Let's make this Higher-Order Component useful by adding all the conditional renderings as enhancement:
You can imagine how this might not be the perfect fit for React Hooks. First, usually a React Hook does not
return conditional JSX. And secondly, a React Hook is not guarding a component from the outside but
rather adds implementation details in the inside.
since we have functions in JavaScript, we can pass more information as arguments from the outside to
gain more control as user of this Higher-Order Component:
if (!props.data.length)
return <div>{dataEmptyFeedback || 'Data is empty.'}</div>;
...
Or
function withStyles(Component) {
return function (props) {
const style = { padding: '0.2rem', margin: '1rem' };
return <Component {...props} style={style} />;
};
}
function Button(props) {
// Apply the style to the <button> element which was added by HOC
return <button style={props.style} type="button">Click Me</button>;
}
function Text(props) {
// Apply the style to the <p> element which was added by HOC
return <p style={props.style}>Hello World!</p>;
}
Keep in mind that while HOCs are a powerful pattern, React has introduced newer patterns like render
props, Custom hook, and Context API that provide alternative ways to achieve similar goals, often with
cleaner and more intuitive syntax.
Render Props
The term ‘render prop’ refers to a technique for sharing reusable code between react components using a
prop whose value is a function. The concept of children as a function or child as a function, also called
render prop in general, is one of the advanced patterns in React. The components which implement this
pattern could be called render prop components.
By using the render props pattern, you can effectively share logic while still allowing each component to
customize its rendering. This promotes code reuse and separates concerns, making your components
more maintainable and flexible which is an alternative to the HOC.
Sharing the same logic across multiple components using the render props pattern involves creating a
component that exposes a render prop, allowing the consuming components to inject their rendering logic
along with the shared functionality or data. Here's how you can achieve this:
Create the Shared Logic Component: Create a component that encapsulates the shared logic and
exposes a render prop.
return <div>{render(sharedData)}</div>;
};
Use the Shared Logic Component in Consuming Components: In each of the components where you
want to use the shared logic, import the SharedLogicComponent and provide a render function that uses
the shared data and implements component-specific rendering logic.
In this example, the SharedLogicComponent encapsulates the shared logic and provides the shared data
to the consuming components. Each consuming component (ComponentA and ComponentB) uses the
shared data along with its own rendering logic to display the content.
<Counter
render={(count, incrementCount) => (
<HoverCounterTwo count={count} incrementCount={incrementCount}
)}
/>
);
}
}
incrementCount = () => {
this.setState((prevState) => ({count: this.state.count + 1}));
}
render () {
return (
<div>
{this.props.render(this.state.count, this.incrementCount)}
</div>
);
}
}
<Counter
{(count, incrementCount) => (
<HoverCounterTwo count={count} incrementCount={incrementCount}
)}
/>
);
}
}
In the above code inside the Counter component we just removed the render prop and as this is a function
it will work as a functional component
Then in the Counter component we have to change the prop render to children like this
incrementCount = () => {
this.setState((prevState) => ({count: this.state.count + 1}));
}
render () {
return (
<div>
{this.props.children(this.state.count, this.incrementCount)}
</div>
);
}
}
As our web app grows, the JavaScript bundle size increases. Thus when we ship our app to production, it'll
have a slow initial page load because it takes a long time for the browser to download the JavaScript
bundle.
To avoid a larger JavaScript bundle, we can use 𝐂𝐨𝐝𝐞 𝐒𝐩𝐥𝐢𝐭𝐭𝐢𝐧𝐠. Code splitting allows us to create
multiple bundles. It helps us 𝐥𝐚𝐳𝐲-𝐥𝐨𝐚𝐝 the things user currently needs, leading to better performance as
we only load the code that the user requires.
imports on React components via the 𝐥𝐚𝐳𝐲() function. For the dynamically imported components, we can
In React, there is a feature to support code splitting and lazy loading. React allows us to use dynamic
wrap them in the 𝐒𝐮𝐬𝐩𝐞𝐧𝐬𝐞 component to show a 𝐟𝐚𝐥𝐥𝐛𝐚𝐜𝐤 UI while we wait for the component to
load.
Code splitting is a feature that helps in splitting the code or components into various bundles and loading
them on demand it reduces the bundle size which saved the bandwidth of the user and reduced the initial
load time of the app because by using the code splitting the browser has to load less javascript.
How to know the page is focused or not?
There’s a javascript built in event listener for focus which gives the boolean status true or false on focus to get
the updated status you can create a simple custom hook as shown in the picture. Some of the use cases for this
are:
1 - To call the get api for updated data ( like react query which calls the api again if you focus the window again)
and always sync with updated data.
2 - to start the animation again if the page is focused again
3 - Client side timer starts/paused on focus.
🔺 2.Detecting unexpected side effects: Hooks like useState, useMemo, and useReducer are invokes the
functions twice in development and once (as expected) in production mode.
This could create some confusion while debugging the code, but by doing this, strict mode makes sure to
check for potential memory leaks.
The DOM element can now be targeted using ref. You can simply attach a ref reference to the element that
you need to target.
🔺4.Recommending the createRef API over the legacy string ref: A wrapped component can’t figure out if its
child component already has a ref. This problem can be solved using a callback ref pattern or a more
modern createRef API instead.
class Form extends Component {
render() {
return <input onClick={() => this.focus()} ref='input' />;
}
focus() {
console.log(this.refs.input.value);
}
}
React.js is a minimal and elegant library. Just create a javascript function to represent a component that
instantly renders, when your data changes. It is simple, but also misleading. In reality, React is a highly
complex UI library, with a great quantity of technical baggage that has accumulated in recent years.
1. Nested Components
return (
<div>
<ChildComponent />
<button onClick={() => setCount(count + 1)}>Count me</button>
</div>
);
};
This nesting of functions actually creates a small performance issue. You see, every time your parent
component is called, it'll re-create the child component's definition. It'll keep on declaring the same function
over and over again on every execution.
return (
<div>
<ChildComponent />
<button onClick={() => setCount(count + 1)}>Count me</button>
</div>
);
};
N.B: Don’t over-define your functions/components if you can only define them once and use them many
times.
2. Props Drilling
Now another problem you may face with big projects is that you may have one component at the very top
that holds your state but then you have a deeply nested component that needs to use that state. What you
need to do is then pass that state as a prop to a lot of intermediate components that do not actually require
it until it finally gets to that child.
This is known as prop drilling because it makes you wish to drill a hole into your brain. One solution is to
bring in a state management library like Redux, Context API, or Zustand. However, the majority of apps
only have a handful of values that are truly global while everything else must be localized.
When I first start building a React app I normally begin by creating one big giant component, typically in the
app component generated by CLI like create-react-app. However, this leads to an anti-pattern of having
one overly large, deeply-nested component. With a component like that, It's hard to understand refactoring
and testing. We can improve that code by refactoring it into reusable components that better represent
what it does. Luckily there is a VSCode extension called Glean or Abracadabra that can take advantage of
the IDE to do this work for us. It's a huge time saver that I wouldn’t want to live without.
<ul>
{items.map((item, index) => <li key={index}>{item.name}</li>)}
</ul>
The example is simple, but notice how we're using the index variable provided by the map method. We
need to provide a key to our elements, so we might as well use this one, right?
Then after you have the unique ID you can rewrite the code like this:
<ul>
{items.map(item => <li key={item.ID}>{item.name}</li>)}
</ul>
Here's a similar example of a component that does too much work and may end up causing performance
issues. Let's think of a component with two different state. On one state, we need to run an expensive
calculation anytime the value changes.
return (
<div>
<button onClick={() => setCount(count + 1)}>Count me</button>
</div>
);
};
The problem with our current execution is that anytime, the state changes on this component, it will rerun
the expensive calculation even though it just depends on the one count value it may not be very obvious
here. In a situation like this, you'll want to use the useMemo hook. What it will do is remember the last
value and just run the actual function when its dependent data changes.
import { useState, useMemo } from 'react';
return (
<div>
<button onClick={() => setCount(count + 1)}>Count me</button>
</div>
);
};
React provides the useCallback hook as well for the same use case. Instead of calling the function
instand, it returns a new function later on we need to call this.
6. Unnecessary Div
return (
<div>
<Button>Close</Button>
</div>
);
return (
<Button>Close</Button>
);
When there are multiple components to be returned, use react fragment or in shorthand form <> as shown
below:
return (
<>
<Button>Close</Button>
<Button>Open</Button>
</>
);
7. DRY Code
Try to avoid duplicate code and create a common component to perform the repetitive task to maintain the
DRY (Don’t Repeat Yourself) code structure. For instance: When you need to show multiple buttons on a
screen then you can create a common button component and use it rather than writing markup for each
button.
8. Unnecessary Comments
Add comments only where it’s required so that you do not get confused while changing code at a later time.
Also don’t forget to remove statements like Console.log, debugger, unused commented code.
9. Inline-styles
12. The first anti pattern is using the component state for everything
The component state should only be used for values that change within the component. Not for values that
come from PROPS or Global State.
These are the people who don't understand the lifecycle methods or don't use them properly. You want to
make sure, you know the order in which the lifecycle methods are called and you use them to your
advantage otherwise you're going to end up with some performance issues and nobody wants that.
These are the ones who just love to overcomplicate things. Keep your components simple folks!. The
simpler the better that way your code is easy to understand and maintain, the most importantly less likely to
make you pull your hair out!
By using this simple method to combine all your Providers into a single Provider element, you can avoid
React's Provider wrapping hell.
Passing Props to Children Components
In React, there are multiple ways to pass props to children components. Two common approaches are
using React.Children.map and render props.
The first method involves using React.Children to iterate over the children and then use
React.cloneElement to clone each child with new props. This approach is not recommended because it is
less type-safe and less intuitive to readers of the code.
The second method involves passing the props to children via render props. The children are passed in as
a function that can accept any arguments you want to pass and returns the actual children. This method is
more type-safe and easier to understand, as it clearly shows the props being passed to the children.
In my personal opinion, the render props approach is a better choice as it provides more clarity and
reduces the likelihood of error. Additionally, it is more flexible and can be used in a wider range of
scenarios.
You can see how to implement both approaches in the code snippet I've attached.
React Composition Pattern
It’s very useful pattern to reduce prop drilling and optimize the performance!
As a refresher on re-renders: React components re-render when their state or props change.
So looking at PostLayout again, children doesn't re-render because it's a prop that hasn't changed.
ScrollToTop Hook
If you've ever used a Single-Page Application (SPA) with multiple routes, you've probably experienced the
issue of the page not automatically scrolling to the top when switching routes. This can be particularly
frustrating for users who expect the new page to start at the top, but instead find themselves in the same
position they were in on the previous page.
The React Hook I created is called ScrollToTop. It uses the useLayoutEffect Hook to scroll the page to the
top whenever the user switches to a new route using react-router-dom. Have a look at the attached code
snippet.
The useLayoutEffect Hook is used instead of useEffect because we want the effect to take place after the
DOM is painted. This is important because if the effect were to take place before the DOM is painted, the
user would briefly see the page in the same position before it is scrolled to the top.
By using useLayoutEffect, the effect is executed after the DOM is painted, which means that the user will
never see the page in the old position.
Object literal is so cool than switch case because it’s so easy to write and maintain and on the other hand
it’s really a combersome to maintain switch cases I’ll always recommend to go for object literal instead of
switch cases it’s so hard to find a example where switch case is better then object literal if there’s any
kindly comment so that we can know but for me object literal is so better than switch case.
Lazy Loading Child Components
In React, parent components are typically loaded before child components. This is because the parent
component is responsible for rendering the child component.
If you want to ensure that the parent component is completely loaded before the child components are
rendered, you can use a technique called conditional rendering.
Conditional rendering involves rendering the child components only when the parent component is ready.
You can do this by using a state variable in the parent component that is set to true once the component is
fully loaded. Then, you can use this state variable to conditionally render the child components.
Here's an example:
function ParentComponent() {
const [isLoaded, setIsLoaded] = useState(false);
useEffect(() => {
// simulate some asynchronous loading
setTimeout(() => {
setIsLoaded(true);
}, 0);
}, []);
return (
<div>
{isLoaded ? <ChildComponent /> : null}
</div>
);
}
In this example, the ParentComponent uses the useState hook to create a state variable called isLoaded,
which is initially set to false. The component then uses the useEffect hook to simulate some asynchronous
loading by setting isLoaded to true after a 0-second delay.
The ParentComponent then conditionally renders the ChildComponent using the isLoaded state variable. If
isLoaded is true, the ChildComponent is rendered. Otherwise, nothing is rendered.
By using conditional rendering in this way, you can ensure that the parent component is completely loaded
before the child components are rendered.
Yes, the technique I described is similar to the lazy loading and suspense techniques in React.
Lazy loading is a technique where components are only loaded when they are needed, rather than all at
once when the app loads. This can improve performance by reducing the initial load time of the app. In lazy
loading, you can use React's lazy function to load a component lazily, and the Suspense component to
render a fallback while the component is being loaded.
function ParentComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<LazyChildComponent />
</Suspense>
</div>
);
}
In this example, the ParentComponent uses the lazy function to lazily load the ChildComponent. The
Suspense component is used to render a fallback (<div>Loading...</div>) while the ChildComponent is
being loaded.
I see many react developers don’t understand how react works under the hood let me explain one of the
core concept of react.
In this very simple example I just created a onClick function and calling setState four (4) times and just
using the function on button click so my question is when I click on the button how many times a
component re renders? And what will be the output.
React uses the automatic batching algorithm to batch the setState calls what does it mean? It means react
batches all the setState call in a function if we use another state like setName(“Aamir”) still it will do the
same it will batch all the setState calls into one and just re renders it one time.
Why? Because of performance think if the component re renders four times which will decrease the
performance and it isn’t necessary to re render multiple times so react does a great job to batch the
setState call isn’t that great?
If we want to get the latest value we have to use the function part like setCount((prev)=>prev+1) In that way
react will still re renders only one time but it has the latest value.
If you did not get it yet how component composition can help with a few re-renders, think at it in this way:
If you have a state in ComponentA and set it, the AnotherComponent will re-render too:
But if the ComponentA takes children props and returns the children props - because AnotherComponent
is passed as children somewhere up the tree - the AnotherComponent won't re-render when the state is set
in ComponentA:
Example 02:
Let's say you have a Post component that displays the scroll progress. It updates the state base on the
scroll event:
return (
<>
<h2 className="progress">
Progress: {progress}%
</h2>
<div className="content">
<h1>Content Title</h1>
{/** more content */}
</div>
</>
)
}
This code will cause a lot of re-renders, and we can assume that the blog post content contains a lot more
components—so re-renders will be expensive.
If we move the logic to a separate component and use component composition to glue them together, the
number of re-renders goes.
All I did was moving the state updates to PostLayout and rendering the post content as a prop.
return (
<>
<h2 className="progress">
Progress: {progress}%
</h2>
{children}
</>
);
};
return (
<Button onClick={onClick}>Click me!</Button>
);
};
The children is nothing more than a prop to the Button component.
Instead of passing down a string to Button, we may want to add an icon to the text as well:
return (
<Button onClick={onClick}>
<img src="/logos/logo.svg" />
Click me!
</Button>
);
};
But we're not limited to the children prop. We can create more specific props that can accept components
as well:
return (
<Button
onClick={onClick}
icon={<img src="/logos/logo.svg" />}
>
Click me!
</Button>
);
};
And that is the essence of component composition: a simple yet incredibly powerful pattern that makes
React components highly reusable.
When working on React projects, you'll continuously find yourself refactoring components to be more
generic through component composition so that you can use them in multiple places.
In React, "controlled" and "uncontrolled" inputs refer to two different ways of managing and handling form
inputs within a React component.
Controlled Inputs: Inputs whose value is directly controlled by React state using the value prop and
updated via the onChange event handler.
Uncontrolled Inputs: Inputs whose value is not directly controlled by React state, and their value is
accessed using the DOM API or ref.
return (
<input
type="text"
value={inputValue}
onChange={handleInputChange}
/>
);
};
In the above example, the input value is managed by the inputValue state variable, and any changes to the
input will trigger the handleInputChange function, which updates the state accordingly.
return (
<>
<input type="text" ref={inputRef} />
<button onClick={handleButtonClick}>Get Input Value</button>
</>
);
};
In this example, the input element has a ref attribute set to inputRef. When the button is clicked, the
handleButtonClick function reads the input's value directly from the inputRef.current.value, allowing you
to access the input's value without managing it through state.
Lifecycle Methods
Lifecycle methods are part of class components and provide ways to interact with the component at various
points in its lifecycle. Common lifecycle methods include componentDidMount, componentDidUpdate,
componentWillUnmount, and more.
Pros:
Fine-grained control: Lifecycle methods allow you to perform specific actions at different points in the
component's lifecycle.
Well-established: Many React developers are familiar with lifecycle methods, as they've been available for
a long time.
Cons:
Boilerplate code: Lifecycle methods can lead to verbose and complex code, especially in larger
components.
Confusing ordering: The sequence of lifecycle methods can be hard to follow, leading to potential bugs and
confusion.
Lifecycle Hooks
Hooks are a newer addition to React and offer a way to use state and other React features without writing a
class. Hooks like useState, useEffect, useContext, and more enable functional components to manage
state and handle side effects.
Pros:
Simplicity: Hooks can simplify your code by allowing you to separate concerns into small, reusable
functions.
No class syntax: Hooks eliminate the need to deal with class syntax, which some developers find
cumbersome.
Code organization: Hooks promote a more modular and organized code structure.
Cons:
Learning curve: If you're new to React, Hooks might introduce a learning curve.
Ecosystem shift: As Hooks are a relatively new addition, some libraries and best practices might still rely
heavily on class components.
Summary:
The decision to use lifecycle methods or Hooks depends on your familiarity with React, the complexity of
your components, and your team's preferences. While Hooks are generally recommended for new projects
due to their simplicity and modularity, there's no need to refactor existing class components that are
working well.
React Batching
React Batching, also known as batched updates, refers to a mechanism used by React to group multiple
state updates and render changes into a single update operation. This process helps to optimize the
rendering performance by reducing the number of actual DOM updates and reflows, which can be
resource-intensive and slow down your application.
When you update the state in a React component, React doesn't immediately perform the update and re-
render the component. Instead, it batches multiple state updates together and performs a single re-render
at the end of the process. This behavior is automatic and behind the scenes, and it's designed to improve
the overall performance of your application.
Let's go through a simple example to demonstrate how React batching works. In this example, we'll use the
automatic batching behavior that React provides.
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
}
In this example, when you click the "Increment" button, the handleClick function is called, which updates
the count state by incrementing it. However, the console.log statement immediately after the setCount call
might not show the updated value of count.
This is because React batches state updates. It doesn't immediately update the count and re-render the
component after each state update. Instead, React waits for the current execution context to finish before
applying the updates and triggering a re-render.
Performance: By grouping multiple state updates and rendering them together, React minimizes the
number of DOM updates and layout recalculations, which leads to improved performance.
Reduced Redundant Rendering: If React performed a re-render for every single state update, it could
lead to redundant rendering and multiple reflows, negatively impacting performance.
Optimized Diffing: When multiple state updates are batched together, React can perform a more efficient
diffing process to determine which parts of the DOM need to be updated.
Function Execution: Batched updates also help avoid unnecessary function calls by only calling lifecycle
methods or effects once for a group of updates.
SoC basically means that each component has its own responsibilities which do not "leak" to other
components. Having a good SoC means that we can move components around, extend and reuse them
easily.
View Model: It’s an object with everything the view/component needs to do its job. The hook and the view
model factory are two different things, even though both expose the model. The hook arranges everything
the factory needs to produce the view model. For example, to build a view model, we need API data. That's
the job of a hook to feed the factory with the API data. When the factory retrieves everything it needs, it
generates a view model that is passed back to the view/React component.
For example:
Let’s look at the abstraction/SoC:
It’s now easily testable code:
Normalizing State
React performance matters, and proper state management can make a huge difference in your
application's speed and efficiency.
By normalizing your state structure, you simplify data retrieval and updates, leading to smoother user
experiences. Keep your React code efficient and enjoy faster apps.
By encapsulating complex array state logic into a reusable custom hook, you not only enhance code
readability but also promote code reusability across your projects.
Before:
After:
Implementation:
Avoid using useState for Complex State Management
Before:
After:
We can also add validation:
1 React Fundamentals
1️⃣
• useState
• useEffects
• useReducer
• useRef
• useContext
• Use custom hooks
3️⃣React Performance
• useCallback
• useMemo
• React.memo
• Lifting state up
• Colocating state
4️⃣React Patterns
5️⃣Core concepts