React is an open-source JavaScript library developed by Facebook for building user interfaces, especially for developing single-page applications. It allows developers to create large web applications that can update and render efficiently in response to data changes.
Components are the building blocks of a React application. Each component is an independent and reusable piece of UI. There are two types of components:
Functional Components – simple functions that return JSX.
Class Components – ES6 classes that extend React.Component
JSX stands for JavaScript XML. It’s a syntax extension that looks similar to HTML and is used with React to describe what the UI should look like. JSX allows mixing HTML with JavaScript.
Example:
The virtual DOM is a lightweight in-memory representation of the real DOM. When a component's state or props change, React first updates the virtual DOM. Then, it calculates the difference (diffing) and updates only the changed parts in the real DOM, improving performance.
Props (short for properties) are used to pass data from parent to child components. They are read-only and help in making components reusable.
In simple words, we can say React Props are like function arguments in JavaScript and attributes in HTML.
To send props into a component, we use the same syntax as HTML attributes:
Add a "language" props to the Codegrill Component:
<Codegrill language="React" />;
The component receives the argument as a props object:
function Codegrill (props) {
return <h2>Welcome to { props.language } world!</h2>;
}
State is a built-in object that stores property values that belong to the component. When the state changes, the component re-renders.
Example:
Feature | Props | State |
Changeable | No | Yes |
Set by | Parent | Component |
Usage | Reusability | Interactivity |
Hooks are functions that let you “hook into” React features in functional components. Common hooks include useState, useEffect, useContext, etc.
useEffect(() => {
document.title = "Hello!";
}, );
With hooks, functional components can manage state and side effects too.
useState is a hook that allows you to add state to a functional component. It returns an array with the current state and a function to update it.
const [count, setCount] = useState(0);
useEffect is used for side effects like fetching data, subscribing to services, or manually changing the DOM. It runs after every render by default.
useEffect(() => {
console.log("Component mounted or updated");
}, );
We can use if, ternary (? :), or logical (&&) operators inside JSX to conditionally render components.
Example:
{isLoggedIn ? <Logout /> : <Login />}
Keys help React identify which items have changed, are added, or removed in a list. It helps in efficiently updating the UI.
{items.map(item => <li key={item.id}>{item.name}</li>)}
Fragments let you group multiple elements without adding an extra node to the DOM.
<>
<h1>Title</h1>
<p>Description</p>
</>
When two components need to share state, it's moved to their closest common ancestor. This is called lifting state up.
Context is a way to pass data through the component tree without having to pass props manually at every level.
const ThemeContext = React.createContext('light');
Prop drilling is passing props through many layers of components unnecessarily. You can avoid it using context or state management libraries like Redux.
Redux is a state management library often used with React. It stores the entire application state in a single store, and state is updated using actions and reducers.
Forms in React are controlled using state. Each input field's value is managed by state and updated via an onChange handler.
React uses camelCase for event names and passes functions directly
<button onClick={handleClick}>Click Me</button>
Reconciliation is the process React uses to update the DOM. It compares the new virtual DOM with the previous one and makes the minimal changes.
React’s reconciliation algorithm uses a diffing strategy on the virtual DOM to efficiently update only the changed parts of the actual DOM. Instead of re-rendering the entire DOM, it does the following things
Compares old and new virtual DOM trees.
Identifies differences (node type, attributes, keys).
Applies minimal updates using document.createElement, appendChild, etc.
This makes updates faster and less expensive than direct DOM manipulation.
A Higher-Order Component is a function that takes a component and returns a new component with extended behavior.
const withLogger = (WrappedComponent) => {
return (props) => {
console.log('Props:', props);
return <WrappedComponent {...props} />;
};
};
const MyComponent = (props) => <div>Hello {props.name}</div>;
export default withLogger(MyComponent);
Used for cross-cutting concerns like authentication, logging, and analytics.
The Context API allows global state management without prop drilling. Ideal for simple use cases like theme, auth, or user settings.
const ThemeContext = createContext('light');
function App() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar() {
const theme = useContext(ThemeContext);
return <div>Theme is {theme}</div>;
}
Use Redux for complex, deeply nested, and large-scale applications.
Custom hooks are functions that start with use and encapsulate reusable logic.
function useCounter(initial = 0) {
const [count, setCount] = useState(initial);
const increment = () => setCount(c => c + 1);
return { count, increment };
}
const { count, increment } = useCounter(5);
Custom hooks improve modularity and readability.
React.memo is a HOC that prevents re-renders if props haven't changed.
const MyComponent = React.memo(({ name }) => {
console.log('Codegrill');
return <div>Hello {name}</div>;
});
Use it for functional components with pure props to optimize performance.
While lifting state up helps share state, in complex apps it can:
Lead to tight coupling
Make parent components bloated
Reduce reusability of child components
In such cases, Context API, Redux, or Recoil is preferred for better separation of concerns.
These mechanisms ensure smooth user experience even in dynamic UIs.
A render prop is a function passed to a component that controls what to render.
function Mouse({ render }) {
const [position, setPosition] = useState({ x: 0, y: 0 });
return <div onMouseMove={e => setPosition({ x: e.clientX, y: e.clientY })}>
{render(position)}
</div>;
}
<Mouse render={({ x, y }) => <h1>Mouse is at {x}, {y}</h1>} />
This promotes reuse without HOCs or duplication.
React Portals render components outside the root DOM hierarchy.
ReactDOM.createPortal(
<ModalContent />,
document.getElementById('modal-root')
);
It allows overlay-like behavior without CSS hacks.
useMemo memoizes the result of a computation.
useCallback memoizes the function definition.
Use useMemo when you have an expensive calculation:
const expensiveValue = useMemo(() => computeExpensiveValue(num), num);
Use useCallback when passing stable functions to child components:
const handleClick = useCallback(() => doSomething(id), id);
Both are performance optimizations and should be used when rerenders hurt performance.
<React.StrictMode> is a development tool that helps:
Warn about deprecated patterns
Verify side effects in useEffect by invoking them twice
<React.StrictMode>
<App />
</React.StrictMode>
It does not affect production builds.
Prop drilling happens when you pass data through many intermediate components that don’t need it, just to get it to the ones that do.
<Parent>
<Child>
<GrandChild data={data} />
</Child>
</Parent>
This can be avoided by using
Keys help React:
Use unique and stable keys like IDs:
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
Avoid using index as key unless items won’t change position.
Controlled Component: Form state is handled by React.
const [value, setValue] = useState('');
<input value={value} onChange={e => setValue(e.target.value)} />
Uncontrolled Component: Form state is handled by the DOM.
<input ref={inputRef} />
Controlled components give better control and validation.
Debouncing limits the rate a function is invoked. Useful for search or resize events.
function useDebounce(value, delay) {
const [debounced, setDebounced] = useState(value);
useEffect(() => {
const handler = setTimeout(() => setDebounced(value), delay);
return () => clearTimeout(handler);
}, value);
return debounced;
}
This can be used in search components to avoid API spamming.
Fragments let you return multiple elements without adding extra nodes.
<>
<h1>I love Codegrill</h1>
<p>Codegrill is awesome!</p>
</>
Or with a key:
<React.Fragment key={item.id}>
<div>{item.name}</div>
</React.Fragment>
Useful when wrapping elements in lists or layouts.
Hydration is the process of React attaching event listeners and making a static HTML page interactive.
Error boundaries catch errors in child components and render fallback UI.
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError(error) {
return { hasError: true };
}
render() {
return this.state.hasError
? <h1>Something went wrong.</h1>
: this.props.children;
}
}
This can be used to catch runtime errors in: