ReactJs is a free and open-source front-end JavaScript library for building user interfaces based on components.
- Why React
- Static vs Dynamic vs SPA
- Why React is a library
- Virtual DOM
- Shadow DOM
- React DOM
- JSX
- XML
- Class Life Cycle Methods
- React Hooks
- Functional components
- Why use functional components over class components
- Prop drilling
- State vs Props
- Strict mode
- React optimisation techniques
- Error boundries
- One way vs Two way Binding
- Controlled vs Uncontrolled components
- React.memo
- Forward ref
- Context API vs Redux
- [Redux]
- [Redux side-effects]
- [Middlewares]
- [Redux toolkit]
- [Redux persists]
- [NextJS]
- [React vs Nextjs]
- [Nextjs rendering methods]
- [Advantages of NextJs]
- [Composition vs Inheritance]
- [Keys]
- [Thinking in React]
- [Accessibility]
- [Code Splitting]
- [PropTypes]
- [Reconciliation]
- [Fragments]
- [State mutation]
- [Use of render()]
- [useContext vs Redux]
- [useState vs useReducer]
- [useMemo vs useCallback]
- [useEffect vs useLayoutEffect]
- [useRef]
- [useImperativeHandle]
- [Styling in React]
- [Custom Hook]
- [React Router]
With React, first we can built the components, then we can connect them together.
- Static webpages are the
prebuilt/pre-uploaded
files that gets served when the user requested. - When we navigate to a specific route, page reloads & the new html file gets served.
- Static sites are very fast but they only make sense when your content changes infrequently.
- These sites generate HTML on the server side through the use of
templating engines
or a language like PHP. - The content is generated per request and is served slower than its static site counterpart.
- The main advantage of a dynamic website is that they do not need to be rebuilt. When your data changes, the content will be shown updated on the next refresh.
- SPAs are applications built in a manner that does not reload the entire page when navigating the site's routes.
- It is a single file that served to the client. It doesn't reload the page rather re-renders the page contents.
- Not good for SEO becoz a web crawler may not wait for the JavaScript to finish executing before it is finished crawling.
React deals with the views
& let's us choose the reest of the front-end architecture.
But by adding few more libraries, we can build a complete app easily & also react has the best community around it.
Let us first understand DOM, it is Document Object Model. It is a structural representation of the HTML elements. DOM represents the entire UI of the applicaton.
Without React we can directly manipulate the DOM elements which results in frequent DOM manipulation, and each time an update was made the browser had to re-calculate & repaint the whole view accr to the new content. So React brought us Virtual DOM, which can be refered as the copy of the actual DOM.
The virtual DOM
(VDOM) is a programming concept where an ideal, or “virtual”, representation of a UI is kept in memory and synced with the “real” DOM by a library such as ReactDOM. This process is called reconciliation
.
The Shadow DOM is a browser technology designed primarily for scoping variables and CSS in web components.
Its a package that provides DOM specific methods to manage the DOM elements. It provides some APIs or methods, such as render, find DOM node etc.
ReactDOM.render(
<React>
<App/>
</React>, document.getElementById('root')
);
- Stands for JavaScript XML.
- It just provides
syntactic sugar
for the React createElement function. - JSX made easy to write both HTML & JavaScript together.
- But browsers cannot understand the JSX, so we have to use transpilers(Babel) to convert JSX to browser understandable code.
- You are not required to use JSX, but JSX makes it easier to write React applications.
const myElement = React.createElement('h1', {}, 'I do not use JSX!');
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(myElement);
const myElement = <h1>I Love JSX!</h1>;
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(myElement);
Stands for Xtensible markup language
.
HTML is a Markup language but XML provides framework to define markup languages.
- Mounting : constructor, static getDerivedStateFromProps, render & componentDidMount.
- Updating: static getDerivedStateFromProps, render, shouldComponentUpdate, getSnapshotBeforeUpdate, componentDidUpdate.
- Unmounting: componentWillUnmount.
- Error handling: static getDerivedStateFromError & componentDidCatch.
Introduced in React 16.8.
Hooks allow function components to have access to state and other React features. Because of this, class components are generally no longer needed.
- useState - useState lets you manage local state within a function component.
- useEffect - With useEffect, you invoke side effects from within functional components.
- useContext - Context is React’s way of handling shared data between multiple components.
- useReducer - useReducer may be used as an alternative to useState. It’s ideal for complex state logic.
- useCallback - useCallback returns a memoized callback function.
- useMemo - useMemo Hook returns a memoized value.
- useRef - The useRef Hook allows you to persist values between renders.
- useImperativeHandle - useImperativeHandle is a React Hook that lets you customize the handle exposed as a ref.
- useLayoutEffect - useLayoutEffect is a version of useEffect that fires before the browser repaints the screen.
- useDebugValue - useDebugValue Hook helps developers to debug custom hooks in Devtools.
- useDeferredValue - useDeferredValue is a React Hook that lets you defer updating a part of the UI.
- useTransition - useTransition is a React Hook that lets you update the state without blocking the UI.
- useId - useId is a React Hook for generating unique IDs that can be passed to accessibility attributes.
- useSyncExternalStore - useSyncExternalStore is a React Hook that lets you subscribe to an external store.
- useInsertionEffect - useInsertionEffect is a version of useEffect that fires before any DOM mutations.
Functional components are stateless(No state & no side effects) components before hooks introduction.
In javascript, classes are not real classes, they are just syntactic sugar, behind they are actually functions itself.
Functions are straight forward, so 3 things actually motivated react team to introduce hooks to provide lifecycle features to functional components.
- Complex components become hard to understand.
- Classes confuse both humans and machines.
- It's hard to resuse stateful logic b/w components.
In Javascript, the 'this' keyword acts differently than other language, so there will be some learning curve for someone who is coming from other languages.
Prop drilling refers to where we have to pass data/state from a top-level component to a deeply nested component.
- States are plain Javascript objects. It allows components to create & manage their own data.
- States are mutable.
- State holds the information about the component.
- State cannot be accessed by the child component.
- Props are also a Javascript object which contains all the properties passed to the child components.
- Props are Immutable.
- Props allow us to pass data from one component to another component.
- Props can be accessed by the child components.
It activates additional checks & warning for its decendents.
It highlights the potential problems in the application.
Yes, in order to detect any problems with the code & warn you about them only in development mode.
-
React.Fragments:
It avoids adding extra nodes to the DOM. -
React.Lazy & React.Suspense:
Lazy loading is a great technique for optimising & speeding up the render time of our app.
The idea of lazy loading is to load components only when it is needed.React.Suspense
allows us to add a fallback content as a loading state. -
React.Memo:
To memoize a component or to cache the component. -
Pagination: When we are rendering large amount of data, implementing pagination decreases the load on the DOM tree.
-
React-window(Virtualize long lists): If we are rendering a very large list, implementing infinte scroll technique using
react-window
helps in rendering only visible lists. -
Using Throttling & debouncing techniques:
-
Using a CDN This will be useful in delivering the static content more quickly & efficiently. Cloudflare is an example.
-
Web workers: Web workers makes it possible to run a script operation in a web browser's background thread, so that the main thread won't get blocked.
-
Server-side rendering:
-
Lazy-loading images:
-
Code splitting:
Is the way to handle errors in react.
We can wrap any part of our application in a special component & if that part of our apllication experiences an uncaught error, it will be shown as error. So we can log these errors & display a fallback UI. & it doesn't break our app.
To use error boundary, we have to write some boilerplate code with class component.
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// You can also log the error to an error reporting service
logErrorToMyService(error, errorInfo);
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
If we don't want to write all these code, we can just use react-error-boundary
npm package.
npm i react-error-boundary
Data Binding is the process of connecting the view element or user interface, with the data which populates it. ReactJS uses one-way data binding.
Data binding in React can be achieved by using a controlled input
. A controlled input is achieved by binding the value to a state variable and a onChange event
to change the state as the input value changes.
We can see two-way binding in AngularJS
. Two-way binding gives components in your application a way to share data. Use two-way binding to listen for events and update values simultaneously between parent and child components.
In a controlled component
, form data is handled by a React component. The alternative is uncontrolled components
, where form data is handled by the DOM itself.
To write an uncontrolled component, instead of writing an event handler for every state update, you can use a ref to get form values from the DOM.
- The component is under control of the component’s state.
- These components are predictable as are controlled by the state of the component.
- Controlled by the parent component.
- Have better control on the form data and values
- Components are under the control of DOM.
- Are unpredictable because during the life cycle methods the data may loss.
- Controlled by the DOM itself.
- Has very limited control over form values and data.
React Memo is a higher-order component that wraps around a component to memoize the rendered output and avoid unnecessary renderings. or,
If our component renders the same result given the same props, we can wrap it in a React.memo HOC.
This improves performance because it memoizes the result and skips rendering to reuse the last rendered result.
There are two ways you can wrap your component with React.memo():
const myComponent = React.memo((props) => {
/* render using props */
});
export default myComponent;
const myComponent = (props) => {
/* render using props */
};
export const MemoizedComponent = React.memo(myComponent);
- Use this to wrap only the child components.
- Memo only checks for prop changes.
- It checks/compares both the type & value.
- If the component is heavy & usually renders with different props.
- The more often the component renders with the same props, the havier & more computationaly expensive the output is, we need memo.
- Use profiling to measure the benefits of applying React.memo.
- When we have a pure functional component, meaning it should output the same results with the same input.
- Donot use when you have a component with often changing props becoz memo have to compares & that is an expensive operation.
Used to pass the reference/ref from the parent component to the child component.
const inputRef = useRef(null)
// inside parent component
<Child ref={inputRef} />
// inside child component
const Child = forwardRef((props, ref) => {
<input ref={ref}/>
})
Redux is an open-source JavaScript library used to manage application state. A Predictable State Container for JS Apps.
The way Redux works is simple. There is a central store that holds the entire state of the application. Each component can access the stored state without having to send down props from one component to another.
3 core components in Redux — actions, store, and reducers.
- Actions: Actions are plain javascript objects. Actions are the only way you can send data from your application to your Redux store.
Actions are created via an action creator, which in simple terms is a function that returns an action. And actions are executed using the store.dispatch() method which sends the action to the store.
We dispatch an action & it includes a type property which indicates the type of action & the payload that contains the information.
const setLoginStatus = () => {
return {
type: 'LOGIN',
payload: {
username: 'neelesh'
}
}
}
// setLoginStatus function is a `action creator` & its returned object is the `action`.
- Reducer:
Reducer is a
pure function
. It is based on the reduce function in JavaScript, where a single value is calculated from multiple values. Reducers contains the business logic.
Once the action is dispatched, it reaches the reducer function. It takes the current state of the application, performs the action & return a new state. Then the new state gets stored the Store.
They do not change the data in the object passed to them or perform any side effect in the application.
- Store: The Store is a central state which is again a JavaScript object that holds the application state.
It is highly recommended to keep only one store in any Redux application.
Actions performed on the state always return a new state. Thus, the state is very easy and predictable.
With Redux, there’s one general state in the store, and every component has access to the store. This eliminates the need to continuously pass state from one component to another.
Context provides a way to pass data through the component tree without having to pass props down manually at every level.
Context API helps in eleminating prop drilling & it is ideal for small applications.
Redux inspired by functional programming & out of the box has no place for side effects. They must be always pure functions.
This brings us some questions:
- How can we enable the state transition of an application via asynchronous actions?
- How do we enable state transitions involving a request to a web server, or the use of a timer?.
- How do we integrate our application state with the data generated by an asynchronous action, while complying with Redux’s architectural pattern?
However, redux middlewares makes it possible to intercept dispatched actions & add additional complex behaviour around them, including side effects.
We can use redux-thunk middleware or Redux toolkit that lets us write action creators with more complex & asynchronous logic.
We can still write asynchronous logic without using any middlewares but thats little messy, it's managable if it's just a small application otherwise better to use middlewares. (https://www.velotio.com/engineering-blog/asynchronous-calls-in-redux-without-middlewares)
Redux middlewares allows developers to intercept all actions dispatched from components before they are passed to the reducer function.
For ex., we might want to sanitize the user’s input before it reaches our store for further processing. This can be achieved via Redux middleware.
Unlike the reducer, middleware is not required to be a pure function, so the Thunk middleware can perform functions that trigger side effects without any problems.
Redux Toolkit is a the official, opinionated, batteries-included toolset for efficient Redux development.
Includes utilities to simplify common use cases like store setup, creating reducers, immutable update logic, and more.
- DRYs up your code.
- You avoid recreating logic, saving time and resources.
- It can create cleaner and more efficient code.
This allows to save the redux store into localstorage.
Next.js is an open-source web development framework created by the private company Vercel providing React-based web applications with server-side rendering and static website generation.
| React | NextJs | | 1. React is a Javascript library to build UI. | 1. NextJs is a React framework for building fullstack applications | | 2. React uses client-side JavaScript to render pages | 2. Next.js uses server-side to pre-render every page | | 3. When client requests the URL, first a blank HTML file will reach the client along with JS & CSS files | 3. HTML gets generated on the server & served to the client | | 4. React doesn't require live server | 4. NextJs needs a live Nodejs server |
By default, Next.js pre-renders every page. This means that Next.js generates HTML for each page in advance, instead of having it all done by client-side JavaScript.
Next.js has two forms of pre-rendering: Static Generation and Server-side Rendering. The difference is in when it generates the HTML for a page.
The HTML page is generated at build time, i.e when we run next build
. This HTML will then be reused on each request. It can be cached by a CDN.
const About = () => {
return <div> About </div>
}
export default About;
The HTML is generated on each request.
SSR should be used when we need to fetch data from the API. NextJs provides a method for data fetching called getServerSideProps
.
export default function Page({ data }) {
// Render data...
}
// This gets called on every request
export async function getServerSideProps() {
const res = await fetch(`https://.../data`)
const data = await res.json()
// Pass data to the page via props
return { props: { data } }
}
one drawback of SSR with data fecthing is, Everytime anyone requests this perticular page, the server has to go out & make a new request to the API to get the data. This results in more work for the server & also more cost for if any 3rd party APIs.
If we don't want the page to get updated every single time, we can use ISR (Incremental Static Regeneration).
This is same as SSR except it includes Caching
. So, instead of getServerSideProps here it uses getStaticProps
.
export const getStaticProps = () => {
const res = await ........;
return {
props: {
person: res.person
},
revalidate: 10
}
}
When user A requests the webpage, he gets served the page with getStaticProps. So when another user requests the same page within the revalidate period (here it is 10sec), he gets served the same static page & not the newly generated page. Once 10sec elapsed, users will get served a new page once they request.
Yes, CSR is also possible. Syntax will be just like React using useEffect.
const CSR = () => {
const [person, setPerson] = useState("")
useEffect(() => {
axios.get('https://some.com').then(res => {
setPerson(res.data.person)
})
}, [])
return (
<div> Hey Iam {person} </div>
)
}
export default CSR;
So the server returns the HTML page with only Hey Iam
content & the once the HTML rendered on the browser, the api gets called & renders the name.
Drawback is again the SEO issue.
- Out of the box routing. File system routing. app.js is the root.
- Out of the box code splitting. Only the requested page will get served to the client.
- Nextjs is a fullstack framework, so we can write both FE & BE in a single project.
- Pre-rendering can result in better performance and SEO.
- Out of the box image optimisation using Image tag.
In Object-Oriented Programming, composition
is a well-known concept. It describes a class that can refer to one or more objects of another class as instances rather than inheriting properties from a base class like inheritance
.
Inheritance is an Object-Oriented Programming concept in JavaScript that allows us to inherit the features of a parent from the child.
React recommends Composition.
Keys help React identify which items have changed, are added, or are removed.
Keys should be given to the elements inside the array to give the elements a stable identity.
React don’t recommend using indexes for keys if the order of items may change. This can negatively impact performance and may cause issues with component state.
Note: If you choose not to assign an explicit key to list items then React will default to using indexes as keys.
- Break The UI Into A Component Hierarchy
- Build A Static Version in React
- Identify The Minimal (but complete) Representation Of UI State
- Identify Where Your State Should Live
- Add Inverse Data Flow