Full Stack Camp
236 subscribers
12 photos
16 files
105 links
Fullstack Camp | Learn. Build. Launch.
Join us for a hands-on journey through HTML, CSS, JavaScript, React, Node.js, Express & MongoDB — all in one place.
Use this bot to search for lessons.
@FullstackCamp_assistant_bot
DM: @Tarikey6
Download Telegram
useEffect

useEffect handles side effects – operations that interact with the outside world or that React cannot manage during rendering. Examples: fetching data, reading localStorage, setting timers, or manually changing the DOM.

useEffect takes two arguments: a function (the effect) and a dependency array. The effect runs after the component renders (or commits to the screen). The dependency array tells React when to re‑run the effect:

Basic Syntax

useEffect(() => { console.log("Mounted"); }, []);



Dependency Array
Empty
[]
Runs once.
With dependency
[count]
Runs when count changes.
No dependency
Runs every render.

Analogy
Dependency array = trigger list.
Like security sensors.
Only activates when chosen values change.

Cleanup Function
Used when component leaves.


useEffect(() => {
const timer = setInterval(() => {
  console.log("Running"); }, 1000);
return () => { clearInterval(timer); }; }, []);


Why?
Prevent memory leaks.
Fetching API Data
Fetching data is a classic side effect, so it belongs inside useEffect (usually with an empty dependency array to run once on mount). While fetching, you typically manage three states: data, loading, and error. This pattern gives users feedback and handles failures gracefully.

useEffect(() => { fetch("https://jsonplaceholder.typicode.com/users")
.then(res => res.json())
.then(data => console.log(data)); }, []);


Axios Version
Install:

npm install axios

Usage

import axios from "axios";


useEffect(() => {
axios.get("https://jsonplaceholder.typicode.com/users")
.then(res => console.log(res.data)); }, []);


useRef

useRef creates a mutable object with a .current property. Unlike state, changing .current does not trigger a re‑render. This makes useRef ideal for:

· Accessing DOM nodes directly (e.g., focusing an input, measuring size).
· Storing values that persist across renders but shouldn’t cause re‑renders (e.g., interval IDs, previous values).

const inputRef = useRef();


Access DOM


<input ref={inputRef} />
<button onClick={() => inputRef.current.focus()}> Focus </button>



Analogy
useRef = sticky note.
Stores info quietly.
No UI update.

useMemo

useMemo caches the result of a computation. It only recomputes when one of its dependencies changes. Use it for expensive calculations (e.g., filtering large arrays, complex math) to avoid doing unnecessary work on every render.

const result = useMemo(() => {
return heavyCalculation(data); }, [data
]);


Why?
Avoid unnecessary recalculation.

Analogy
Like saving previous math answer.
No need to solve again.
🧩 Week 9 Day 2 Challenges

Challenge 1: Student Registration App

➢Build a small app where users can register students.
Requirements:
➛Create a form with:
name
age
course
➛Use useState for all inputs
On submit:
➢prevent page refresh
➢add student to a displayed list
➢Use .map() to render all students
➢Show total students registered
➢Add conditional message:
➢if no students → show "No students yet"


Challenge 2: Product Search Dashboard

Build a product list dashboard.
Requirements:
➛Create an array of at least 8 products
➛Render them using .map()
➛Add search input
➛Use onChange to filter products by name
➛Use conditional rendering:
➛if no product matches → show "No products found"
➛Use useMemo to optimize filtered results
➛Add a button that focuses search input using useRef

Challenge 3: User Fetch + Profile Viewer

Build a user viewer app.
Requirements:
➛Fetch users from: https://jsonplaceholder.typicode.com/users
➛Use useEffect
➛Display:
name
email
company
➛Add loading state
Add button:
➤show/hide users
➤Add input field to search users by name
➤Use conditional rendering
➤Add cleanup function inside useEffect
➤Use keys properly when rendering list


When you are done,
💥 Share your solutions,
💥 invite a friend,
and as always —

💥 stay well, stay curious, and stay coding ✌️
2
Forwarded from Bytephilosopher
Today I had a meeting with a senior Google developer who has worked there for more than 20 years, both as a permanent employee and freelancer. It was such an amazing experience, and I got a lot of valuable insights from him.

Here are some life principles and lessons I learned:

1. Stay fit and take care of your health. At the end of the day, health matters the most.

2. Focus on building real skills first. Don’t just follow whatever trend the world is chasing. Work on something you truly enjoy, something you can spend hours on like it’s a game. Read books, especially research papers. If you are a junior developer, learn the fundamentals deeply. Use AI wisely to understand things from the ground level.

3. Communication skills are just as important as technical skills. Learn how to explain and present your knowledge. These days, your online presence matters a lot more than just a resume. Build visibility through platforms like GitHub and by sharing your work online.

Also, attend workshops and meetups. But don’t just attend. make sure you connect with at least one person and keep in touch with them.

4. Have a spiritual life. Whether you are religious or not, always try to be good to others. You never know when your kindness will come back to help you.

Another important point was this:
Build projects that solve your own problems or local problems around you. It’s easier to understand problems you personally face. Then think about how those solutions can scale internationally.

And about AI:
Use AI as a mentor, not as something to completely depend on. Learn the basics well and use AI to simplify your learning and productivity.

Finally, being a good developer means being a lifelong learner. Technology changes fast, and you must keep updating yourself. He started learning during the COBOL and FORTRAN era, and he still adapts to modern technologies today. So keep learning, keep building, and prepare yourself for the international market.

Visibility matters. Real projects matter. Continuous learning matters.

@byte_philosopher
3👍2
1
Full Stack Camp
Photo
CommonJS Vs ES Modules

JavaScript has two main module systems: CommonJS and ES Modules.

CommonJS is the older system, built for Node.js. It uses require() to load a module and module.exports to share code. When you write const fs = require('fs'), Node reads the file right away, line by line. This is synchronous, meaning it waits for each module to load before moving to the next line. CommonJS works well on the server because files are on your local disk and load quickly. Many old Node projects still use it, and you don't need any special settings.

ES Modules are the newer, official standard for JavaScript. They work in both modern browsers and Node.js (with "type": "module" in package.json or using .mjs files). Instead of require(), you write import { something } from './file.js' and export your code. ES Modules load asynchronously, so they can fetch multiple files at once without blocking. This makes them better for web pages and large applications. They also allow tools to remove unused code, a feature called tree shaking, which keeps your final bundle small.

The big difference between them is where and how they run. CommonJS is synchronous and works out of the box in Node, while ES Modules are asynchronous and work in both browsers and Node with a little setup. CommonJS uses require() and module.exports; ES Modules use import and export. For new projects, ES Modules are the better choice because they follow the standard and will work everywhere. But you still see CommonJS everywhere, especially in older Node code and many npm packages.

stay well, stay curious, and stay coding ✌️
👍3
Forwarded from Messi Bre
Eid Mubarak fam 🤗
4
Week 9 Day 3 — Component Composition & Routing

Hello campers
Today we’ll learn how React applications are organized like real systems.

React applications are built from small reusable pieces connected together.

Part 1 — Component Composition

Component Composition
Composition means combining smaller components to build larger UI structures. You split your UI into pieces like <Navbar />, <Card />, <Button /> and then nest them inside each other. For example:


function Dashboard() {
  return (
    <div>
      <Sidebar />
      <MainContent />
    </div>
  );
}



This keeps code clean, reusable, and easy to scale — just like building with LEGO blocks.

Beginner Mistake
Putting everything inside one huge component (e.g., 500 lines in App) makes it messy, hard to debug, and impossible to reuse.

Better Approach
Break UI into pieces, each with one responsibility:

App
├── Navbar
├── Sidebar
├── Feed
└── Footer


Thinking in Components
Before coding, ask “What parts repeat?” Those become reusable components. Example: instead of writing <h2>John</h2><h2>Sarah</h2> twice, create:


function UserCard({ name }) {
  return <h2>{name}</h2>;
}
// Then reuse:
<UserCard name="John" />
<UserCard name="Sarah" />


This prevents duplication and makes your app modular and predictable.

➜Why Composition Matters
Because real apps grow. Composition gives you readability, maintainability, scalability, and reusability.

➜ Shared State Problem
Two sibling components (like SearchBar and ProductList) often need the same data but can’t talk directly. Without a solution, updating SearchBar won’t filter ProductList.
Example:

<SearchBar />   {/* wants to update search term */}
<ProductList /> {/* wants to read search term */}


➜ Lifting State Up
Move shared state to the closest common parent (here, App). Parent holds useState, passes data and setter functions down via props.

function App() {
  const [search, setSearch] = useState("");
  return (
    <>
      <SearchBar search={search} setSearch={setSearch} />
      <ProductList search={search} />
    </>
  );
}

Now SearchBar calls setSearch and ProductList reads search — single source of truth, both stay synced. Analogy: one shared fridge instead of each person having fake copies.


➜ props.children
A special prop that lets you pass JSX content inside a component’s tags. Inside the wrapper, you render {props.children}. Example:

function Card(props) {
  return <div className="card">{props.children}</div>;
}
// Usage – different content, same wrapper:
<Card><h1>Hello</h1></Card>
<Card><button>Buy</button></Card>

This makes wrapper components reusable for any inner content.

➢ Part 2 — Routing

Routing
Routing determines which component to show for a specific URL. You define routes like /home → Home component, /about → About. Without routing, your React app is just one page; routing turns it into a multi‑page experience without full reloads.

React Router DOM
The standard library for routing in React. Install with:

npm install react-router-dom

Then wrap your app with <BrowserRouter> in main.jsx so React can track URLs. React itself has no built‑in routing — this library fills that gap.

React Router DOM v6 – main tools

🔹 BrowserRouter – Wraps your entire app to enable routing.

import { BrowserRouter } from "react-router-dom";
<BrowserRouter><App /></BrowserRouter>

🔹 Routes & Route – Define which component renders for which path.

<Routes>
  <Route path="/" element={<Home />} />
  <Route path="/about" element={<About />} />
</Routes>

🔹 Link – Navigation without page reload (replaces <a>).

<Link to="/about">About</Link>

Regular <a> refreshes the whole page and loses state — Link keeps your app fast.

🔹 NavLink – Same as Link but adds active styling automatically.

<NavLink to="/about" className={({ isActive }) => isActive ? "active" : ""}>
  About
</NavLink>

Perfect for navigation bars where the current page should look different.
Dynamic Routes
Routes that capture variable parts of the URL, like /users/1, /users/42.
Define with a colon:

<Route path="/users/:id" element={<User />} />

Inside the User component, use useParams to grab the id:

import { useParams } from "react-router-dom";
function User() {
  const { id } = useParams();
  return <h1>User ID: {id}</h1>;
}

This lets you show different user profiles using the same component.

useNavigate
A hook to change routes programmatically from JavaScript.

const navigate = useNavigate();
// After login:
navigate("/dashboard");
// Or go back:
navigate(-1);

Useful after form submissions, logins, or any action where you need to redirect without a link click.

➜Mental Model of Routing
React Router watches the URL. When it changes, it renders the matching component — no full page refresh. It feels instant.

Real App Structure

src/
├── components/   # reusable: Button, Card, Navbar
├── pages/        # full screens: Home, About, Profile
├── App.jsx
└── main.jsx

Pages are entire views that match a route; components are smaller pieces reused across pages. This keeps your project clean and easy to navigate.
🧩 Week 9 Day 3 Challenges

Challenge 1: Movie Explorer App

Build a small multi-page movie app.
Requirements:
➜Create pages:
Home
Movies
MovieDetails
➜Use React Router DOM
➜Add navigation using Link or NavLink
➜Store movie data in an array or fetch from an API
➜Render movie cards using .map()
Clicking a movie should navigate to:
/movies/:id
➜Use useParams to display selected movie details
➜Create a reusable Card component using props.children

Challenge 2: Travel Destination Planner

Build a travel destinations app.
Requirements:
➜Create components:
Navbar
DestinationList
DestinationDetails
➜Use routing for pages
➜Add at least 5 destinations
➜Clicking a destination opens:
/destinations/:id
➜Use useNavigate for a “Go Back” button
➜Lift state up to parent component for selected region filtering
➜Use props.children for reusable layout wrappers

Challenge 3: Music Artist Dashboard

Build a music artist dashboard.
Requirements:
➜Create pages:
Home
Artists
ArtistProfile
➜Use dynamic routes:
/artists/:id
➜Display:
artist name
genre
albums
➜Use .map() to render artist list
➜Use useParams for profile page
➜Add active navigation styling using NavLink
➜Create reusable Section wrapper component using props.children
Lift search state up so multiple components can access filtered artists


When you are done,
💥 Share your solutions,
💥 invite a friend,
and as always —

💥 stay well, stay curious, and stay coding ✌️
We are back after months. Big apologies for getting lost 🙏🙏
👍1
🌟 Week 9 Day 4 --- Advanced React: Context, Performance & Real-World Patterns

Hello campers 💙

Today we’ll tackle the challenges every large app faces:

· Global state (without drilling props through 10 levels)
· Advanced state logic (like a spreadsheet for your data)
· Custom reusable logic (your own hooks)
· Performance (stop wasted re-renders)
· Portal modals, Error Boundaries, Lazy Loading, and more.

Let’s dive in!


Part 1 --- State Management Deep Dive

⚠️ The Prop Drilling Problem

Remember lifting state up? It works, but what if your component tree is 5 levels deep?

<App>
  <Layout>
    <Sidebar>
      <UserMenu>
        <Avatar user={user} /> {/* user had to travel all the way down */}
      </UserMenu>
    </Sidebar>
  </Layout>
</App>


Passing user through components that don’t even use it is called prop drilling. It’s messy and hard to refactor.

Analogy: Like giving a message to a receptionist, who gives it to a manager, who gives it to a team lead, just to reach the developer. Waste of time.

🧠 Context API --- The Solution

Context provides a way to share data across the entire component tree without passing props manually.

Steps to Use Context

1. Create the Context

import { createContext, useContext } from "react";

const UserContext = createContext();


2. Provide the Context (wrap your parent)

function App() {
  const [user, setUser] = useState({ name: "Megersa" });

  return (
    <UserContext.Provider value={{ user, setUser }}>
      <Dashboard />
    </UserContext.Provider>
  );
}


3. Consume the Context (in any child)

function Avatar() {
  const { user } = useContext(UserContext);
  return <h1>{user.name}</h1>;
}


No more drilling! 🎉

Analogy: Context is like a company-wide announcement system. Instead of whispering down the hallway (props), you broadcast it to everyone who cares (useContext).


⚙️ useReducer --- Complex State Logic

useState is great for simple data (strings, numbers). But when you have complex state with multiple sub-values or transitions (e.g., "ADD_ITEM", "REMOVE_ITEM", "UPDATE_TOTAL"), useReducer is your friend.

Basic Syntax

import { useReducer } from "react";

// 1. Define a reducer function
function cartReducer(state, action) {
  switch (action.type) {
    case "ADD":
      return { ...state, count: state.count + 1 };
    case "REMOVE":
      return { ...state, count: state.count - 1 };
    default:
      return state;
  }
}

function Cart() {
  // 2. useReducer returns [state, dispatch]
  const [state, dispatch] = useReducer(cartReducer, { count: 0 });

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: "ADD" })}>+</button>
      <button onClick={() => dispatch({ type: "REMOVE" })}>-</button>
    </div>
  );
}


Analogy: useState is a light switch (on/off). useReducer is a TV remote—lots of buttons (actions) that change the screen (state) in predictable ways.


🛠️ Custom Hooks --- Reusable Logic

If you find yourself repeating logic across components (e.g., fetching data, tracking window size, managing local storage), extract it into a Custom Hook.

Rules:

· Must start with use (e.g., useFetch).
· Can use other hooks inside (useState, useEffect, useContext).

Example: useFetch


import { useState, useEffect } from "react";

function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetch(url)
      .then(res => res.json())
      .then(data => { setData(data); setLoading(false); })
      .catch(err => { setError(err); setLoading(false); });
  }, [url]);

  return { data, loading, error };
}
// Usage in any component
function Users() {
  const { data, loading, error } = useFetch("https://api.example.com/users");
  if (loading) return <p>Loading...</p>;
  return <div>{data.map(user => <p key={user.id}>{user.name}</p>)}</div>;
}
Part 2 --- Performance Optimizations

React is fast, but unnecessary re-renders can slow you down. Here’s how to stop them.

🔹 React.memo --- Component Caching

React.memo is a higher-order component. It prevents a component from re-rendering if its props haven't changed.
jsx
// Without memo: re-renders every time parent re-renders.
const ExpensiveComponent = ({ data }) => {
  console.log("Rendering!");
  return <div>{data}</div>;
};

// With memo: only re-renders if 'data' changes.
const MemoizedComponent = React.memo(ExpensiveComponent);

Warning: Memoization isn't free. Use it only for components that re-render often with the same props.

---

🔹 useCallback --- Memoizing Functions

When you pass a function as a prop, it gets re-created on every render. This breaks React.memo because the prop looks different every time.
jsx
// Bad: Creates a new function every render
function Parent() {
  const handleClick = () => console.log("clicked");
  return <Child onClick={handleClick} />;
}

// Good: useCallback caches the function
function Parent() {
  const handleClick = useCallback(() => {
    console.log("clicked");
  }, []); // Empty array = never changes
  return <Child onClick={handleClick} />;
}

Analogy: useCallback is like giving someone a permanent business card. Without it, you hand them a new one every time they see you (pointless).

---

🔹 useTransition --- Non-Urgent Updates

Sometimes state updates cause lag (e.g., filtering a giant list while typing). useTransition lets you mark certain updates as "low priority" so they don't block the UI.
jsx
import { useState, useTransition } from "react";

function SearchPage() {
  const [query, setQuery] = useState("");
  const [filteredList, setFilteredList] = useState([]);
  const [isPending, startTransition] = useTransition();

  const handleChange = (e) => {
    const value = e.target.value;
    setQuery(value); // Urgent update (typing)
   
    startTransition(() => {
      // Low priority update (filtering large array)
      const results = hugeList.filter(item => item.includes(value));
      setFilteredList(results);
    });
  };

  return (
    <div>
      <input value={query} onChange={handleChange} />
      {isPending && <p>Loading results...</p>}
    </div>
  );
}

---

Part 3 --- DOM & UI Mastery

🚪 Portals --- Rendering Outside the Parent

Sometimes you need to render something outside the root div (e.g., modals, tooltips, dropdowns) to avoid CSS clipping or z-index issues.

ReactDOM.createPortal lets you render a component anywhere in the DOM.
jsx
import { createPortal } from "react-dom";

function Modal({ children, isOpen }) {
  if (!isOpen) return null;
  // Render this modal inside the "modal-root" div instead of the parent tree
  return createPortal(
    <div className="modal-overlay">
      <div className="modal">{children}</div>
    </div>,
    document.getElementById("modal-root") // Must exist in index.html
  );
}

Analogy: Portals are like walkie-talkies. Even though you're in one room (parent component), you broadcast your message (modal UI) to another room (modal-root) seamlessly.

---

🪞 forwardRef & useImperativeHandle --- Advanced Refs

Sometimes you need to access a DOM element inside a child component. forwardRef lets the parent pass a ref down.
jsx
// Child component
const FancyInput = forwardRef((props, ref) => {
  return <input ref={ref} className="fancy" {...props} />;
});

// Parent component
function Parent() {
  const inputRef = useRef();
  useEffect(() => {
    inputRef.current.focus(); // Focusing the child's input
  }, []);
  return <FancyInput ref={inputRef} />;
}

useImperativeHandle lets you limit what the parent can do with the ref (like exposing only focus and clear).

const FancyInput = forwardRef((props, ref) => {
  const inputRef = useRef();
 
  useImperativeHandle(ref, () => ({
    focus: () => inputRef.current.focus(),
    clear: () => { inputRef.current.value = ""; }
  }));

  return <input ref={inputRef} />;
});
✍️ Uncontrolled Components

You learned controlled components (state drives the input). Uncontrolled components let the DOM handle the input's value; you just read it via ref.

function UncontrolledForm() {
  const nameRef = useRef();

  const handleSubmit = (e) => {
    e.preventDefault();
    alert(nameRef.current.value); // Read value directly from DOM
  };

  return (
    <form onSubmit={handleSubmit}>
      <input ref={nameRef} defaultValue="Megersa" />
      <button type="submit">Submit</button>
    </form>
  );
}

When to use: Simple forms where you don't need live validation per keystroke. It's less code but less control.

---

Part 4 --- Stability & Tooling

🚨 Error Boundaries --- Catching Crashes

If a component crashes, the whole React app unmounts (blank screen). Error Boundaries catch JavaScript errors in their child tree and display a fallback UI.

Note: Only works in class components (but you can use libraries like react-error-boundary for functions).

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    console.log("Log error to service:", error);
  }

  render() {
    if (this.state.hasError) {
      return <h1>Something went wrong. Please refresh.</h1>;
    }
    return this.props.children;
  }
}

// Usage
<ErrorBoundary>
  <RiskyComponent />
</ErrorBoundary>

Analogy: Error Boundaries are like circuit breakers in your house. Instead of the whole house (app) going dark when one outlet (component) shorts, the breaker just cuts that one circuit.

---

💤 Suspense & Lazy Loading --- Code Splitting

Why load all your JavaScript at once? With React.lazy, you can load components only when they are needed (e.g., the About page only loads when the user clicks "About").

import { lazy, Suspense } from "react";

// This component will be loaded dynamically
const About = lazy(() => import("./pages/About"));

function App() {
  return (
    <div>
      <Suspense fallback={<div>Loading page...</div>}>
        <About />
      </Suspense>
    </div>
  );
}

Analogy: Lazy loading is like a buffet. Instead of putting every dish on your plate at once (initial load), you go back to the buffet table (server) only when you want the dessert (new page).

---

🧰 Legacy Patterns (HOCs & Render Props)

Before hooks existed, we used these patterns. You might see them in older codebases.

Higher-Order Component (HOC): A function that takes a component and returns a new component with extra props.

function withAuth(Component) {
  return function AuthenticatedComponent(props) {
    const [user] = useContext(UserContext);
    if (!user) return <p>Please login</p>;
    return <Component {...props} user={user} />;
  };
}

Render Props: A prop that is a function returning JSX.

<DataFetcher url="/users">
  {(data) => <div>{data.map(...)}</div>}
</DataFetcher>

Pro Tip: Hooks (useContext, useEffect) replace both of these in modern React. Just know what they are when reading legacy code!

---

🛠️ React DevTools & StrictMode

React DevTools (browser extension):

· Inspect component trees (props, state, hooks).
· Profile performance to see which components re-render.
· Highlight updates to track unnecessary renders.

StrictMode (wrapped in main.jsx):
· Runs extra checks in development (e.g., detects unsafe lifecycles, warns about legacy refs).
· Important: It double-invokes effects in dev to help you catch bugs (don't panic, it's just a test!).

<React.StrictMode>
  <App />
</React.StrictMode>
🧩 Week 9 Day 4 Challenges


Challenge 1: Creature Pokedex & Team Builder

Build a Pokémon-style creature collector where users search, filter, and build a battle team.

Requirements:
➜ Use useEffect + fetch to pull creatures from an API (e.g., PokéAPI or similar). Use a custom useFetch hook.
➜ Use dynamic routing: /creature/:id to show detailed stats using useParams.
➜ Manage your "Battle Team" (max 6) using useReducer (actions: ADD, REMOVE, REORDER). Share this state globally via Context.
➜ Use useMemo to calculate average stats (HP, Attack) of your team efficiently.
➜ Use React.memo on the creature card list to prevent re-renders while typing in the search filter.
➜ Lazy-load the "Battle Simulator" page using React.lazy + Suspense.
➜ Render a stats comparison modal using createPortal (outside the root div).
➜ Use useNavigate for a "Go Back" button on detail pages.

---

Challenge 2: Recipe Box & Meal Planner

A cooking app where users browse recipes, filter by cuisine, and plan meals for the week.

Requirements:
➜ Use useState for controlled search/filter inputs, and useRef to focus the search bar on mount.
➜ Use dynamic routes: /recipe/:slug for detailed instructions using useParams.
➜ Manage the "Weekly Meal Plan" (Monday–Sunday) using useReducer (actions: ADD_MEAL, REMOVE_MEAL, CLEAR_DAY). Share via Context.
➜ Use useMemo to calculate total prep time and calorie count for the week's plan.
➜ Use useTransition to keep the cuisine-filter dropdown smooth when filtering a large recipe list.
➜ Create a custom useLocalStorage hook to persist the meal plan automatically.
➜ Lazy-load the "Grocery List" page (generates shopping items from the plan).
➜ Use forwardRef to allow the parent to clear the search input from outside.

---

Challenge 3: Productivity Dashboard (Goal & Habit Tracker)

Track daily habits and long-term goals with live analytics — far beyond a simple checklist.

Requirements:
➜ Use useReducer to manage habits (actions: LOG_HABIT, DELETE_HABIT, EDIT_GOAL). Use Context to share across stats and list components.
➜ Use useMemo to compute streaks, completion rates, and progress percentages without recalculating on every keystroke.
➜ Use useCallback to memoize the logHabit and deleteHabit functions passed to memoized child components.
➜ Use useRef to store the previous week's completion rate for comparison (without re-rendering).
➜ Wrap your habit list items with React.memo.
➜ Implement an Error Boundary to catch failures in the chart/stats component and show a fallback.
➜ Use createPortal to render a quick-add modal that floats above the dashboard.
➜ Use dynamic routing: /goals/:id to view a specific goal's detailed history via useParams.




When you are done,
💥 Share your solutions,
💥 invite a friend,
and as always —

💥 stay well, stay curious, and stay coding ✌️
In 2026, making your website visible and navigable to AI agents is essential because modern users no longer just search the web; they deploy autonomous AI assistants to discover, compare, and summarize services for them. If your application cannot be easily parsed by AI crawlers, it effectively becomes invisible to the next generation of web traffic.

To achieve this discoverability safely, you must strictly separate your public content from your protected data. Ensure that public-facing pages use Server-Side Rendering (SSR) to deliver fully formed HTML, leverage standard robots.txt files to guide permitted bots, and implement Schema.org JSON-LD structured data so AI agents can instantly comprehend your app's core purpose without guessing.

Security remains completely uncompromised during this process though because AI agents interact with your platform exactly like anonymous, logged-out visitors. While bots crawl your public semantic structures, your backend continues to shield private user data behind secure, server-side HttpOnly JWT cookies. Any attempt by an AI crawler to access private endpoints - such as user dashboards or checkout screens - will be instantly blocked with a 401 Unauthorized response since the bot lacks the necessary session cookies.

By explicitly structuring your public routes for machine readability while leaving your authentication walls intact, you can safely maximize your application's reach in the AI-driven ecosystem.
🌟 Week 9 Day 5 --- Redux & Zustand

Good Evening campers 💙

Part 1 --- Redux Toolkit (RTK)

Redux has been the standard for large-scale React applications for nearly a decade, centralizing application state in a single object called the "store." Instead of mutating this object directly, you dispatch "actions" that describe changes, and pure "reducers" compute the next state based on these actions. This unidirectional data flow simplifies debugging with Redux DevTools. However, classic Redux involved extensive boilerplate, requiring action creators, constants, and complex update logic for each feature. Redux Toolkit (RTK) addresses this by providing sensible defaults, using Immer for simpler updates, and automatically generating actions from reducers, resulting in significantly less code while maintaining predictability.

Installing Redux Toolkit and React-Redux

npm install @reduxjs/toolkit react-redux

Creating a Slice (the modern reducer)

A slice bundles together a piece of state, its reducers, and the actions that trigger them. Think of it as a self-contained module for a specific domain—like user, products, or cart.

// store/userSlice.js
import { createSlice } from "@reduxjs/toolkit";

const initialState = {
  name: "Guest",
  isLoggedIn: false,
  preferences: { theme: "dark" }
};

const userSlice = createSlice({
  name: "user",
  initialState,
  reducers: {
    login: (state, action) => {
      // Thanks to Immer, we can "mutate" the state directly!
      state.name = action.payload.name;
      state.isLoggedIn = true;
    },
    logout: (state) => {
      state.name = "Guest";
      state.isLoggedIn = false;
    },
    toggleTheme: (state) => {
      state.preferences.theme = state.preferences.theme === "dark" ? "light" : "dark";
    }
  }
});

// Export the generated action creators
export const { login, logout, toggleTheme } = userSlice.actions;

// Export the reducer to be included in the store
export default userSlice.reducer;

Configuring the Store

The store is the central registry of all your application's state. You combine all your slices into a single root reducer and pass it to configureStore, which automatically sets up the Redux DevTools and middleware like Redux Thunk for async logic.

// store/index.js
import { configureStore } from "@reduxjs/toolkit";
import userReducer from "./userSlice";
import cartReducer from "./cartSlice";

export const store = configureStore({
  reducer: {
    user: userReducer,
    cart: cartReducer
  }
});

Providing the Store to Your React App

Wrap your entire application with the <Provider> component from React-Redux. This gives every component in the tree access to the store using hooks.

jsx
// main.jsx
import React from "react";
import ReactDOM from "react-dom/client";
import { Provider } from "react-redux";
import { store } from "./store";
import App from "./App";
ReactDOM.createRoot(document.getElementById("root")).render(
  <Provider store={store}>
    <App />
  </Provider>
);

Using State and Dispatching Actions in Components

Inside any component, you read state with useSelector and send actions with useDispatch. The selector function subscribes to the Redux store and automatically re-renders your component only when the selected data changes—giving you fine-grained performance control without manual memoization.

jsx
// components/UserProfile.jsx
import { useSelector, useDispatch } from "react-redux";
import { login, logout, toggleTheme } from "../store/userSlice";

function UserProfile() {
  const dispatch = useDispatch();
  const { name, isLoggedIn, preferences } = useSelector((state) => state.user);

  const handleLogin = () => {
    dispatch(login({ name: "Megersa" }));
  };

  return (
    <div>
      <p>Welcome, {name}!</p>
      <p>Theme: {preferences.theme}</p>
      <button onClick={handleLogin}>Login</button>
      <button onClick={() => dispatch(logout())}>Logout</button>
      <button onClick={() => dispatch(toggleTheme())}>Toggle Theme</button>
    </div>
  );
}
Part 2 --- Zustand: The Minimalist Powerhouse

Zustand (German for "state") provides global state management without the complexity of Redux. Instead of using providers and separate action/reducer files, Zustand allows you to create a store with a single custom hook that defines state and mutation functions through a simple API. There are no dispatch functions or boilerplate reducers—just a plain JavaScript object with methods for updates. This simplicity leads to a shallower learning curve and faster prototyping. Zustand also optimizes component re-renders by preventing updates unless the relevant state changes, making it ideal for mid-sized applications and MVPs.

Installing Zustand
bash
npm install zustand

Creating a Store with Zustand

Stores are created using the create function. You provide a callback that receives set and get functions, and returns your state object along with methods to modify it. Notice how everything lives in one cohesive block—no separate actions or reducers.
jsx
// store/useUserStore.js
import { create } from "zustand";

const useUserStore = create((set, get) => ({
  // State
  name: "Guest",
  isLoggedIn: false,
  preferences: { theme: "dark" },
  todos: [],

  // Actions (methods that update state)
  login: (name) => set({ name, isLoggedIn: true }),

  logout: () => set({ name: "Guest", isLoggedIn: false }),

  toggleTheme: () => set((state) => ({
    preferences: {
      ...state.preferences,
      theme: state.preferences.theme === "dark" ? "light" : "dark"
    }
  })),

  addTodo: (text) => set((state) => ({
    todos: [...state.todos, { id: Date.now(), text, done: false }]
  })),

  // Using get to access current state inside actions
  getTodoCount: () => get().todos.length
}));

export default useUserStore;

Using Zustand in Components

To use the store inside a component, you invoke the custom hook and destructure exactly the pieces you need. Zustand's selector pattern ensures your component only re-renders when those specific fields change—similar to Redux's useSelector but built-in.

jsx
// components/Dashboard.jsx
import useUserStore from "../store/useUserStore";

function Dashboard() {
  // Select only what you need—no unnecessary re-renders!
  const { name, isLoggedIn, login, logout, toggleTheme, todos } = useUserStore();

  // Or select a single field with a selector function
  const todoCount = useUserStore((state) => state.todos.length);
return (
    <div>
      <p>User: {name}</p>
      <p>Todo count: {todoCount}</p>
      <button onClick={() => login("Megersa")}>Login</button>
      <button onClick={logout}>Logout</button>
      <button onClick={toggleTheme}>Toggle Theme</button>
    </div>
  );
}
🧩 Week 9 Day 5 Challenges

Challenge 1: Bookmark Manager with Redux Toolkit & Express

Build a bookmark storage app where users can save, tag, and organize their favorite links.

Requirements:
➜ Set up an Express server with GET /api/bookmarks, POST /api/bookmarks, and DELETE /api/bookmarks/:id endpoints. Use an in-memory array (or JSON file) for storage.

➜ Use Redux Toolkit to create a bookmarksSlice with createAsyncThunk for fetching, adding, and deleting bookmarks from your Express backend.

➜ Configure the Redux store with the bookmarks reducer and provide it to your app via <Provider>.

➜ Create a BookmarkList component that uses useSelector and useDispatch to display the bookmarks, show loading/error states, and handle form submission for adding new links.


Challenge 2: Habit Tracker Dashboard with Zustand & LocalStorage Sync

Track daily habits with persistence and a clean dashboard—no backend required for this one, just Zustand plus localStorage.

Requirements:
➜ Create a Zustand store called useHabitStore with state: habits (array), loading (boolean), error (string). Add actions: fetchHabits (reads from localStorage), addHabit, toggleHabit (mark done/undone), and deleteHabit.

➜ Use a custom hook useLocalStorage inside your Zustand actions to sync the habits array to localStorage automatically after every mutation. On initial load, fetchHabits should pull from localStorage.

➜ Use useMemo inside the HabitStats component to calculate the completion rate, current streak, and total active habits without recalculating on every habit toggle.

➜ Use useTransition to mark the habit filter (All / Active / Completed) as a low-priority update, ensuring the toggle buttons remain responsive even with 100 habits.

➜ Use createPortal to render a "Quick Add" modal that floats above the dashboard when the user presses the + button.

Challenge 3: Recipe Finder with RTK Query & Express

Build a recipe discovery app that searches a remote API (or your own Express mock) and caches results intelligently.

Requirements:
➜ Set up an Express server with GET /api/recipes?search=query that returns an array of recipe objects (use a mock dataset or the Spoonacular API).

➜ Use RTK Query's createApi with fetchBaseQuery to define a getRecipes query endpoint and a saveRecipe mutation endpoint for saving favorites.

➜ Use the auto-generated useGetRecipesQuery hook in your SearchPage component. Pass the search term as a query parameter. RTK Query will automatically cache results.

➜ Use useGetRecipesQuery's isFetching and isError flags to show loading spinners and error messages gracefully.

➜ Use useMemo to compute a list of unique recipe categories from the returned data to display as filter chips.

➜ Use useCallback to memoize the search handler that updates the search term state.

➜ Lazy-load the RecipeDetail page using React.lazy and <Suspense> so the user only loads the heavy description and image gallery when they click a recipe.

➜ Use NavLink with active styling to highlight the "Search" and "Favorites" navigation items.

When you are done,
💥 Share your solutions,
💥 invite a friend,
and as always —

💥 stay well, stay curious, and stay coding ✌️
1
Performance Testing

Website performance directly impacts user retention, conversion rates, and search engine rankings - studies show that a 1-second delay can reduce conversions by 7% and customer satisfaction by 16%.

Slow sites also hurt Core Web Vitals, which Google uses as ranking signals, meaning poor performance pushes your pages down in search results.

Beyond SEO, performance affects accessibility: users on slow connections or older devices (common in emerging markets) will abandon a laggy site within 3 seconds. By proactively testing with DevTools, you catch bottlenecks like render-blocking scripts, oversized images, or memory leaks before they reach production, saving engineering hours and preserving brand trust.

Using DevTools as Your Performance Lab

Chrome DevTools gives you a complete performance toolkit without third-party costs.

Start in the Performance tab: hit record, interact with your page (scroll, click, type), stop, and analyze the flame chart - red bars indicate long tasks that block the main thread, while yellow spikes reveal heavy JavaScript or layout recalculations.
Pair this with Lighthouse for automated audits that grade your site and provide actionable fixes (e.g., "defer offscreen images" or "reduce unused CSS").

Use the Network tab's waterfall to spot slow API calls or large assets, and throttle CPU/network to simulate 4G or mid-tier phones.

Finally, check the Coverage tab to find dead code you can strip. Make this a habit: test after every significant change, set performance budgets (e.g., LCP under 2.5s), and treat DevTools as your first line of defense - not a last-minute panic tool.