Component-Based Architecture
React is built around the concept of reusable components. Components are the building blocks of React applications, encapsulating both the structure (HTML) and behavior (JavaScript) of a part of the user interface[1][2].
Components can be as simple as a button or as complex as an entire page. They can be nested within each other, allowing developers to create complex UIs from smaller, manageable pieces. This modular approach makes it easier to maintain and scale applications.
Virtual DOM
One of React's most powerful features is its use of a Virtual DOM (Document Object Model). Instead of directly manipulating the browser's DOM, React creates a lightweight copy of it in memory[1][2].
When a component's state changes, React first updates the Virtual DOM. Then, it compares the updated Virtual DOM with a snapshot of the previous version (a process called "diffing"). Finally, it calculates the most efficient way to apply these changes to the real DOM. This process, known as reconciliation, significantly improves performance by minimizing direct manipulation of the DOM, which can be slow and resource-intensive.
Unidirectional Data Flow
React follows a unidirectional data flow. This means that data in a React application flows in a single direction: from parent components to child components[1].
This approach makes it easier to track where data comes from and how it changes over time, which helps in debugging and understanding the application's state. It also promotes a more predictable state management, as changes to the data can only happen in one way.
JSX
React uses JSX, a syntax extension for JavaScript that allows you to write HTML-like code within your JavaScript files[1]. For example:
const element = <h1>Hello, world!</h1>;
JSX makes it easier to describe what the UI should look like, and React takes care of rendering the actual DOM elements. While JSX is not required to use React, it significantly improves the development experience and code readability.
State and Props
React components can have two types of data: state and props.
State is data that can change over time. It's managed within a component and can be updated using the
setState()
method (in class components) or the useState
hook (in functional components)[1].Props (short for properties) are data passed from a parent component to a child component. Props are read-only, meaning a component should not modify the props it receives[1].
What happens if the prop passed in changed in a functional component?
If the prop changes in a functional component, React will re-render the component to reflect the updated prop values. This allows the component to react to the new data and update its output accordingly.
What happens if we call the functional component in two different places?
If we call the functional component in two different places, React will render two separate instances of the component, each with its own state and props. This allows each instance to function independently.
Lifecycle Methods and Hooks
React components go through a series of stages during their lifetime, from being created and mounted to the DOM, to being updated, and finally being unmounted and destroyed.
Class components use lifecycle methods like
componentDidMount
, componentDidUpdate
, and componentWillUnmount
to hook into these stages[1].Functional components use Hooks, introduced in React 16.8, to achieve similar functionality. Hooks like
useEffect
allow you to perform side effects in function components, replacing several lifecycle methods[1].React Component Lifecycle
Write the feature like useEffect in class declaration in React
In React, class components use lifecycle methods to achieve the same effects as the
useEffect
hook in functional components. useEffect
serves multiple purposes based on how it's configured: it can run effects after every render, only once, or on certain updates depending on specified dependencies. Here's how you can replicate these behaviors using class component lifecycle methods:1. Effect after Every Render
Equivalent to
useEffect(() => { ... })
without dependencies.- Class Component Method:
componentDidMount
andcomponentDidUpdate
class MyComponent extends React.Component { componentDidMount() { // Code to run on component mount } componentDidUpdate() { // Code to run on every update } render() { return <div>...</div>; } }
2. Effect Only Once (On Mount)
Equivalent to
useEffect(() => { ... }, [])
.- Class Component Method:
componentDidMount
jsxCopy code class MyComponent extends React.Component { componentDidMount() { // Code to run only once after the component mounts } render() { return <div>...</div>; } }
3. Effect with Cleanup
Equivalent to using a return function in
useEffect
for cleanup purposes, such as unsubscribing from subscriptions or clearing timers.- Class Component Method:
componentDidMount
for setting up andcomponentWillUnmount
for cleanup
jsxCopy code class MyComponent extends React.Component { componentDidMount() { // Setup code, e.g., starting a timer this.timerID = setInterval(() => this.tick(), 1000); } componentWillUnmount() { // Cleanup code, e.g., clearing a timer clearInterval(this.timerID); } tick() { // Do something every second } render() { return <div>...</div>; } }
4. Effect On Specific Updates Only
Equivalent to
useEffect(() => { ... }, [deps])
where deps
are dependencies that trigger the effect when changed.- Class Component Method:
componentDidUpdate
- You must manually compare props or state to see if they have changed.
jsxCopy code class MyComponent extends React.Component { componentDidUpdate(prevProps, prevState) { // Only run code when specific prop or state has changed if (prevProps.userID !== this.props.userID) { this.fetchUserData(this.props.userID); } } fetchUserData(userID) { // Fetch user data } render() { return <div>...</div>; } }
Example: Converting a useEffect
with Dependencies
If you have a functional component using
useEffect
like so:jsxCopy code useEffect(() => { const subscription = props.source.subscribe(); return () => { subscription.unsubscribe(); }; }, [props.source]);
The equivalent class component using lifecycle methods would be:
jsxCopy code class MyComponent extends React.Component { componentDidMount() { this.subscribe(); } componentDidUpdate(prevProps) { if (this.props.source !== prevProps.source) { this.unsubscribe(); this.subscribe(); } } componentWillUnmount() { this.unsubscribe(); } subscribe() { this.subscription = this.props.source.subscribe(); } unsubscribe() { if (this.subscription) { this.subscription.unsubscribe(); } } render() { return <div>...</div>; } }
In this way, you can replicate any
useEffect
behavior using class component lifecycle methods, although the syntax and specifics of implementation differ slightly.Conclusion
React's component-based architecture, Virtual DOM, unidirectional data flow, and state management make it a powerful tool for building dynamic user interfaces. By breaking UIs into reusable components and efficiently updating the DOM, React allows developers to create fast, scalable, and maintainable web applications.
Understanding these core concepts is crucial for effectively using React and leveraging its full potential in your projects. As you delve deeper into React development, you'll discover more advanced features and patterns that build upon these fundamental principles.
A minimal React Framework From Scratch
Citations
[1] https://www.simplilearn.com/tutorials/reactjs-tutorial/what-is-reactjs
[2] https://www.uxpin.com/studio/blog/how-react-works/
[3] https://blog.hubspot.com/website/react-js
[4] https://www.reddit.com/r/reactjs/comments/zobme1/what_react_framework_do_you_guys_suggest_to/
[5] https://tsh.io/blog/how-does-react-work/