Let's make React components super easy to understand! We've got two main types: Functional Components and Class Components. Think of them as two ways to build your UI pieces.
1. Making a Basic Component ποΈ
Functional Component: Simple JavaScript Function
This is a normal JavaScript function that gives you back UI.
-
How to make:
- Write an arrow function.
- Give it a name (that's your component!).
- It returns some JSX (your UI bits).
- Export/Import: Works just like any other JavaScript file. Easy peasy!
import React, { useState, useEffect } from 'react';
const MyAwesomeFunctionComponent = () => {
return (
<>
<h1>Hello from Functional Fun! π</h1>
</>
);
};
export default MyAwesomeFunctionComponent;
Class Component: Normal JavaScript Class
This is a normal JavaScript class that extends React.Component
.
-
How to make:
- Make a class that
extends React.Component
. - It must have a
render()
method inside that returns JSX.
- Make a class that
- Export/Import: Same as Functional Components.
import React from 'react';
class MyCoolClassComponent extends React.Component {
render() {
return (
<>
<h1>Hello from Classy Code! π©</h1>
</>
);
}
}
export default MyCoolClassComponent;
2. Sending Data with Props π
Props are how you send information from a parent component down to its child.
Functional Component: Props as Arguments
Props come as an argument to your function. Grab them directly!
const FunctionalComponent = ({ name, location }) => { // Props are right here!
return (
<>
<p>Name: {name}</p>
<p>Location: {location}</p>
</>
);
};
export default FunctionalComponent;
// How you'd use it: <FunctionalComponent name="Alice" location="Wonderland" />
Class Component: Props with this.props
For Class Components, props are in this.props
. You'll need a constructor.
-
Constructor magic: The constructor gets props before
render
. -
super(props)
is key! β¨ You must writesuper(props)
. This ensuresthis.props
is ready to use everywhere in your component.
import React from 'react';
class UserClass extends React.Component {
constructor(props) {
super(props); // β¨ IMPORTANT: Makes props available with 'this.props'
}
render() {
const { name, location } = this.props; // Access props using 'this.props'
return (
<>
<h2>Class Based User</h2>
<p>Name: {name}</p>
<p>Location: {location}</p>
</>
);
}
}
export default UserClass;
// How you'd use it: <UserClass name="Bob" location="Builderland" />
3. Handling Changing Data with State π
State is like a component's personal memory. It tracks data that changes, updating the UI.
Functional Component: useState
Hook
The useState
"Hook" adds memory to functional components.
-
Making state:
const [thing, setThing] = useState(initialValue);
-
thing
is your current value. -
setThing
is the function to change it.
-
-
Using state: Just use
thing
. -
Updating state: Call
setThing(newValue);
import React, { useState } from 'react';
const FunctionalComponent = ({ name, location }) => {
const [count, setCount] = useState(0); // 'count' is state, 'setCount' updates it
return (
<>
<p>{name}</p>
<p>Count: {count} π’</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<p>{location}</p>
</>
);
};
export default FunctionalComponent;
Class Component: this.state
& this.setState
In Class Components, state is a big object: this.state
.
-
Making state: Put
this.state = { ... };
in the constructor (when the class starts).
// Inside your Class Component's constructor
this.state = {
count: 0, // Your state variable
userName: "Guest", // Can have many!
};
-
Using state: Access as
this.state.count
or destructure:const { count } = this.state;
. -
Updating state: NEVER change
this.state.count = this.state.count + 1
directly! π« Usethis.setState()
.-
this.setState()
takes an object with new state values.
-
import React from 'react';
class UserClass extends React.Component {
constructor(props) {
super(props);
// Creating state variables inside the constructor
this.state = {
count: 0,
};
}
render() {
const { name, location } = this.props;
const { count } = this.state; // Grab 'count' from state
return (
<>
<h2>Class Based Component</h2>
<p>Name: {name}</p>
<p>Location: {location}</p>
<p>Count: {count} π’</p>
<button
onClick={() => {
// Updating state variables using this.setState()
this.setState({
count: this.state.count + 1,
});
}}
>
Increment
</button>
</>
);
}
}
export default UserClass;
4. Talking to the Outside World: API Calls and Side Effects π
Sometimes, components need to do things beyond rendering UI, like fetching internet data. These are "side effects".
Functional Component: useEffect
Hook for API Calls
useEffect
is your tool for side effects like API calls in functional components.
-
Why
useEffect
? Component shows up fast, thenuseEffect
fetches data and updates the display. -
When it runs for API calls:
- Use
[]
as the second argument. This makes the effect run only once after the first render. Perfect for fetching initial data!
- Use
import React, { useState, useEffect } from 'react';
const FunctionalComponent = ({ name, location }) => {
// State to hold GitHub user data
const [githubUser, setGithubUser] = useState(null);
// π GitHub User API Call (runs once on page load)
useEffect(() => {
const fetchGithubUser = async () => {
// Fetch data for a specific GitHub user (e.g., 'octocat')
const response = await fetch('https://api.github.com/users/octocat');
const data = await response.json();
setGithubUser(data); // Set fetched data to state
};
fetchGithubUser();
}, []); // π¨ Empty array means: Run ONLY once after the first render
return (
<>
<p>{name}</p>
<p>{location}</p>
{/* Display GitHub user info if available */}
{githubUser && (
<div>
<h3>GitHub User Info:</h3>
<p>Username: **{githubUser.login}**</p>
<p>Name: {githubUser.name || 'N/A'}</p>
<p>Bio: {githubUser.bio || 'No bio provided.'}</p>
<p>Public Repos: {githubUser.public_repos}</p>
<p>Followers: {githubUser.followers}</p>
</div>
)}
</>
);
};
export default FunctionalComponent;
Class Component: Life Cycle Methods
Class Components have special methods called Life Cycle Methods that run at different moments in their "life."
5. Class Component Life Cycle (The Component's Journey)
A component has three main phases:
- Mounting: When it first appears on your screen. π
- Updating: When its data (state or props) changes and it needs to refresh. π
- Unmounting: When it disappears from your screen. π§Ή
5.1. Mounting Life Cycle (When it appears π)
When a Class Component mounts:
constructor()
: First thing called! Sets up initial state.render()
: Draws the component (might show empty data first).componentDidMount()
: Called after the component (and its children) are fully visible.componentDidMount is the Best place for API calls. Fetch real data here; component is visible, update state to re-render.
Actual Flow:
- Parent
constructor
-
Parent
render
(Parent starts drawing)- First Child
constructor
- First Child
render
- Second Child
constructor
- Second Child
render
- React updates the actual web page (DOM) β¨ (This is the fast part where React figures out changes)
-
First Child
componentDidMount
(First child is ready) -
Second Child
componentDidMount
(Second child is ready)
- First Child
Parent
componentDidMount
(Parent is ready, after all its kids)
import React from 'react';
class UserClass extends React.Component {
constructor(props) {
super(props);
console.log("UserClass: Constructor called.");
}
componentDidMount() {
console.log("UserClass: componentDidMount called. data!");
/
// β° Setting up an interval (will need cleanup!)
this.timer = setInterval(() => {
console.log('Hello from Class Component interval!');
}, 1000);
}
render() {
const { name, location } = this.props;
console.log("UserClass: Render called.");
return (
<>
<h2>Class Based Component (Life Cycle Demo)</h2>
<p>Name: {name}</p>
<p>Location: {location}</p>
</>
);
}
}
export default UserClass;
5.2. Updating Life Cycle (When it changes π)
When component data (state or props) changes, it refreshes:
render()
: Called again with new props/state.componentDidUpdate(prevProps, prevState)
: Called right after update. You get old props/state. Handy for doing something only if specific data changed.componentDidUpdate(prevProps, prevState)
: Callingthis.setState
incomponentDidUpdate
must be in anif
statement to prevent endless loops.
// Inside your UserClass component...
componentDidUpdate(prevProps, prevState) {
console.log("UserClass: componentDidUpdate called.");
// Example: Fetch new data ONLY if 'location' prop changed
if (this.props.location !== prevProps.location) {
console.log("Location changed! Need new data...");
// Call your API function here
}
// Example: Do something if 'count' state changed
if (this.state.count !== prevState.count) {
console.log("Count just changed to:", this.state.count);
}
}
5.3. Unmounting Life Cycle (When it disappears π§Ή)
When a component is removed from the screen (e.g., navigating away):
-
componentWillUnmount()
: Called just before the component vanishes. Cleanup Timeπ§Ό You must cleanup anything running in the background like asetInterval
. Stop these processes here.
// Inside your UserClass component...
componentWillUnmount() {
console.log("UserClass: componentWillUnmount called. Time to clean up! π§Ή");
clearInterval(this.timer); // Stop the timer we started in componentDidMount
}
Cleanup: How it Works in Both Types π€
Handling background tasks (like timers or subscriptions) is crucial to prevent performance issues, especially in Single Page Applications (SPAs). Here's how cleanup is managed in both component types:
Functional Components (using useEffect
's return)
- If your
useEffect
sets up an ongoing task (likesetInterval
or an event listener), youreturn
a function fromuseEffect
. - This returned function automatically runs when the component unmounts (is removed from the screen) or just before the effect runs again (if its dependencies change).
-
Example: Stopping a timer
useEffect(() => { const timer = setInterval(() => { console.log('Functional component interval running!'); }, 1000); return () => { // This is the cleanup function! clearInterval(timer); // Stops the timer console.log('Functional component interval cleaned up! π'); }; }, []); // Empty array means runs once on mount, cleans up on unmount
Class Components (using componentWillUnmount
)
- If you set up an ongoing task in
componentDidMount
(or other lifecycle methods), you must manually stop it in thecomponentWillUnmount()
method. - This method is specifically designed to run just before the component is completely removed from the DOM.
-
Example: Stopping a timer
class MyClassComponent extends React.Component { componentDidMount() { this.timer = setInterval(() => { console.log('Class component interval running!'); }, 1000); } componentWillUnmount() { // This is the cleanup method! clearInterval(this.timer); // Stops the timer console.log('Class component interval cleaned up! π§Ή'); } // ... render method ... }
Why Cleanup is Super Important: Any background task (like a timer) started in a component will keep running in memory even after the component is gone if not stopped. This causes performance problems in Single Page Applications (SPAs). Always clean up!
Conclusion: Which One to Use? π€
While Class Components were the original way, Functional Components with Hooks (like useState
and useEffect
) are now the modern, recommended way in React!
They are:
- Simpler: Less code, easier to read.
- Cleaner: Logic for one feature stays together.
- Modern: The way forward in React development.
But hey, knowing Class Components is still super useful for understanding old code or if you just prefer that style for certain things!
Top comments (0)