রিয়েক্ট অ্যাপ – API থেকে ডেটা কল করা এবং দেখানো

এর আগে রিয়েক্ট অ্যাপ ডেভেলপমেন্টে সূচনা লেখায় দেখেছি কিভাবে রিয়েক্ট অ্যাপ তৈরি করতে হয়। এবার দেখব কিভাবে API থেকে ডেটা দেখানো যায়।

যে ফোল্ডারে রিয়েক্ট অ্যাপ তৈরি করব, ঐ ফোল্ডারে ন্যাভিগেট করে নিচের কমান্ড লিখবঃ

npx create-react-app .

এরপর আমরা চাইলে সদ্য তৈরি করা রিয়েক্ট অ্যাপ দেখতে পারবঃ

npm start

প্রজেক্টের src ফোল্ডারের ভেতর অনেক গুলো ফাইল দেখব App.js এবং index.js ছাড়া বাকি ফাইল গুলো ডিলেট করে দিব। index.js অলরেডি App কম্পোনেন্ট রেন্ডার করে। আমরা এই App.js ফাইলে আমাদের কোড লিখব। যেমন আমরা API কল করে কিছু ডেটা দেখাবো। এখান থেকে Dummy Post দেখাতে পারি। তার জন্য নিচের মত করে লিখবঃ

import React, { useEffect, useState } from "react";

function App() {
  const [posts, setPosts] = useState([]);
  const [selectedPost, setSelectedPost] = useState(null);

  // Fetch posts from API
  useEffect(() => {
    fetch("https://dummyjson.com/posts")
      .then((res) => res.json())
      .then((data) => setPosts(data.posts));
  }, []);

  return (
    <div style={{ padding: "20px", fontFamily: "sans-serif" }}>
      <h1>📰 Dummy Posts</h1>

      {selectedPost ? (
        <div>
          <button onClick={() => setSelectedPost(null)}>⬅ Back</button>
          <h2>{selectedPost.title}</h2>
          <p>{selectedPost.body}</p>
        </div>
      ) : (
        <ul>
          {posts.map((post) => (
            <li
              key={post.id}
              onClick={() => setSelectedPost(post)}
              style={{
                marginBottom: "10px",
                cursor: "pointer",
                background: "#f5f5f5",
                padding: "10px",
                borderRadius: "8px",
              }}
            >
              <strong>{post.title}</strong>
              <p>{post.body.slice(0, 60)}...</p>
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

export default App;

এখানের কোড গুলো সম্পর্কে জানা যাক।

import React, { useEffect, useState } from "react";

এখানে কয়েকটা জিনিস আমরা ইম্পোর্ট করেছি। রিয়েক্ট হচ্ছে রিয়েক্টের কোর লাইব্রেরী, useState হচ্ছে রিয়েক্ট হুক, যা একটা কম্পপোনেন্টের মধ্যে ডেটা স্টোর করতে সাহায্য করে। অনেকটা ভ্যারিয়েবলের মত। useEffect আরেকটা হুক, যা নির্দিষ্ট সময়ে কোন কোড রান করতে সাহায্য করে।

প্রতিটা কম্পোনেন্ট ডিক্লেয়ার করতে হয় function FunctionName ব্যবহার করে। আমাদের App কম্পোনেন্ট ডিক্লেয়ার করেছি এভাবেঃ

function App() {
..

এর ভেতরেই এই কম্পোনেন্টের UI লজিক গুলো লিখব।

স্টেট ভ্যারিয়েবল

এর পরের অংশ হচ্ছে স্টেট ভ্যারিয়েবলঃ

const [posts, setPosts] = useState([]);
const [selectedPost, setSelectedPost] = useState(null);

স্টেট ভ্যারিয়েবল পরিবর্তন হলে UI পুনরায় রেন্ডার হয়। যেমন কোন ভ্যালু পরিবর্তন হয়েছে, UI কে যদি না জানানো হয় যে কোন ভ্যালু পরিবর্তন হয়েছে, তাহলে UI তো আগের ডেটাই দেখাবে। এর জন্য দরকার হয় স্টেট ভ্যারিয়েবলের। স্টেট ভ্যারিয়েবলের দুইটা অংশ। যেমন

const [posts, setPosts] = useState([]);
  • posts → শুরুতে এই ভ্যারিয়েবলের ভ্যালু । এই ক্ষেত্রে একটা শূন্য অ্যারে ( [] )।
  • setPosts → এটা হচ্ছে একটা ফাংশন, যা ব্যবহার করে এই posts এর ভ্যালু আপডেট করব।

ফলে যতবার আমরা setPosts ব্যবয়াহ্র করে কোন ডেটা সেট করব, ততবার UI রি-রেন্ডার হবে।

useEffect হুক

useEffect(() => {
  // this code runs after the component renders
});

যখনি এই App কম্পোনেন্ট রেন্ডার হবে, তখনি এই useEffect রান হবে। রিয়েক্টে কোন স্টেট ভ্যারিয়েবল আপডেট হলেই পুরো ভিউ রি-রেন্ডার হয়। এর ফলে useEffect কন্টিনিউয়াসলি রেন্ডার হবে। useEffect ব্যবহার করে অনেক ধরনের কাজ করা যায়, যেমন API থেকে ডেটা ফেচ করা, DOM ম্যানুফুলেট করা ইত্যাদি। API থেকে ডেটা ফেচ করার কথা চিন্তা করি, যতবার UI রেন্ডার হবে, ততবার যদি ডেটা ফেচ করি, তাহলে তো অনেকবার API এন্ডপয়েন্টে হিট করবে। যেটা একটা ভালো সিস্টেম হতে পারে না। আর সে জন্য আমরা useEffect এর সাথে একটা ডিপেন্ডেন্সি যোগ করা যায়।

useEffect(() => {
  // side effect code
}, [dependencies]);

আমরা একটা শূন্য অ্যারে ডিপেন্ডেন্সি হিসেবে সেট করেছিঃ

 // Fetch posts from API
  useEffect(() => {
    fetch("https://dummyjson.com/posts")
      .then((res) => res.json())
      .then((data) => setPosts(data.posts));
  }, []);

এর মানে যখন এই কম্পোনেন্টটা মাউন্ট হবে, তখনি ডেটা ফেচ করবে। যতবার রেন্ডার হবে, ততবার না।

UI রেন্ডার করার জন্য JSX লিখতে হয় return (…) স্টেটমেন্টের ভেতর।

return (
  <div style={{ padding: "20px", fontFamily: "sans-serif" }}>
    <h1>📰 Dummy Posts</h1>

    {selectedPost ? (
      ...
    ) : (
      ...
    )}
  </div>
);

এখানে একটা টার্নারি অপারেটর ব্যবহার করা হয়েছে। যখন selectedPost এ কোন ভ্যালু থাকে, মানে নট নাল তখন সিঙ্গেল পোস্টের ডিটেইলস ভিউ দেখিয়েছে। আর যখন নাল, তখন পোস্ট গুলো লিস্ট আকারে দেখিয়েছে। এই তো।

এখন আমরা যদি প্রজেক্টটা রান করি, তাহলে দেখব পোস্ট গুলোর লিস্ট দেখা যাচ্ছে। কোন একটা পোস্টে ক্লিক করলে তা সম্পর্কে বিস্তারিত জানা যাচ্ছে।

কম্পোনেন্ট স্টাইলিং

স্টাইল দেওয়ার জন্য আমরা CSS কোড লিখতে পারি। আবার কোন ফ্রেমওয়ার্কও ব্যবহার করতে পারি। প্রথমে দেখি কিভাবে নিজেদের CSS ব্যবহার করা যায়। একটা ফাইল তৈরি করি App.css নামেঃ

/* App.css */

.app-container {
  padding: 20px;
  font-family: sans-serif;
  background-color: #fafafa;
  min-height: 100vh;
}

.app-title {
  font-size: 28px;
  font-weight: bold;
  margin-bottom: 20px;
}

.post-list {
  list-style: none;
  padding: 0;
}

.post-item {
  margin-bottom: 10px;
  cursor: pointer;
  background: #f5f5f5;
  padding: 10px;
  border-radius: 8px;
  transition: background 0.2s ease;
}

.post-item:hover {
  background: #e3e3e3;
}

.back-button {
  margin-bottom: 16px;
  background-color: #1976d2;
  color: white;
  border: none;
  padding: 8px 12px;
  border-radius: 5px;
  cursor: pointer;
}

.back-button:hover {
  background-color: #125aa0;
}

.post-detail {
  max-width: 700px;
}

.post-detail h2 {
  margin-bottom: 10px;
  font-size: 24px;
}

.post-detail p {
  font-size: 16px;
  line-height: 1.6;
}

এবার এই App.css আমাদের App.js কম্পোনেন্টে ইম্পোর্ট করে নিব import "./App.css"; ব্যবহার করে। এরপর এভাবে ব্যবহার করতে পারিঃ

import React, { useEffect, useState } from "react";
import "./App.css";

function App() {
  const [posts, setPosts] = useState([]);
  const [selectedPost, setSelectedPost] = useState(null);

  useEffect(() => {
    fetch("https://dummyjson.com/posts")
      .then((res) => res.json())
      .then((data) => setPosts(data.posts));
  }, []);

  return (
    <div className="app-container">
      <h1 className="app-title">📰 Dummy Posts</h1>

      {selectedPost ? (
        <div className="post-detail">
          <button className="back-button" onClick={() => setSelectedPost(null)}>
            ⬅ Back
          </button>
          <h2>{selectedPost.title}</h2>
          <p>{selectedPost.body}</p>
        </div>
      ) : (
        <ul className="post-list">
          {posts.map((post) => (
            <li
              key={post.id}
              className="post-item"
              onClick={() => setSelectedPost(post)}
            >
              <strong>{post.title}</strong>
              <p>{post.body.slice(0, 60)}...</p>
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

export default App;

এখানে আমাদের App.js একটা কম্পোনেন্ট। এর ভেতর দুইটা প্রধান অংশ রয়েছে। পোস্ট কার্ড এবং ডিটেইলস ভিউ। হতে পারে এই পোস্ট কার্ড আমাদের অন্যান্য কম্পোনেন্টেও ব্যবহার করা লাগতে পারে। তো এর জন্য আমরা আলাদা আলাদা কম্পোনেন্ট হিসেবে তৈরি করতে পারি। যেন এই কম্পোনেন্ট গুলো রিইউজ করতে পারি।

এর জন্য src ফোল্ডারের ভেতর components নামে একটা ফোল্ডার তৈরি করব। তার ভেতর PostCard.js নামে একটা ফাইল তৈরি করে পোস্ট কার্ডের জন্য এভাবে কোড লিখতে পারিঃ

// src/components/PostCard.js
import React from "react";
import "../App.css";

function PostCard({ post, onSelect }) {
  return (
    <div className="post-card" onClick={() => onSelect(post)}>
      <h3 className="post-title">{post.title}</h3>
      <p className="post-snippet">{post.body.slice(0, 80)}...</p>
      <div className="post-footer">
        <span>💬 {post.reactions.likes + post.reactions.dislikes}</span>
        <span>🧑‍💻 {post.userId}</span>
      </div>
    </div>
  );
}

export default PostCard;

এরপর PostDetailCard.js এর জন্য এভাবে লিখতে পারিঃ

// src/components/PostDetailCard.js
import React from "react";
import "../App.css";

function PostDetailCard({ post, onBack }) {
  return (
    <div className="post-detail-card">
      <button className="back-button" onClick={onBack}>
        ⬅ Back
      </button>
      <h2 className="detail-title">{post.title}</h2>
      <p className="detail-body">{post.body}</p>
      <div className="detail-footer">
        <span>💬 {post.reactions.likes + post.reactions.dislikes}</span>
        <span>🧑‍💻 User: {post.userId}</span>
      </div>
    </div>
  );
}

export default PostDetailCard;

এবার src/App.js ফাইলে উপরের দুইটা কম্পোনেন্ট ব্যবহার করবঃ

import React, { useEffect, useState } from "react";
import "./App.css";
import PostCard from "./components/PostCard";
import PostDetailCard from "./components/PostDetailCard";

function App() {
  const [posts, setPosts] = useState([]);
  const [selectedPost, setSelectedPost] = useState(null);

  useEffect(() => {
    fetch("https://dummyjson.com/posts")
      .then((res) => res.json())
      .then((data) => setPosts(data.posts));
  }, []);

  return (
    <div className="app-container">
      <h1 className="app-title">📰 Dummy Posts</h1>

      {selectedPost ? (
        <PostDetailCard post={selectedPost} onBack={() => setSelectedPost(null)} />
      ) : (
        <div className="post-grid">
          {posts.map((post) => (
            <PostCard key={post.id} post={post} onSelect={setSelectedPost} />
          ))}
        </div>
      )}
    </div>
  );
}

export default App;

স্টাইলিং এর জন্য src/App.css এভাবে লিখতে পারিঃ

.app-container {
  max-width: 1000px;
  margin: 0 auto;
  padding: 20px;
  font-family: sans-serif;
}

.app-title {
  text-align: center;
  margin-bottom: 30px;
}

.post-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 20px;
}

.post-card {
  background: #f8f9fa;
  padding: 16px;
  border-radius: 10px;
  cursor: pointer;
  transition: all 0.2s ease;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
}

.post-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
}

.post-title {
  margin-bottom: 8px;
}

.post-snippet {
  color: #555;
}

.post-footer {
  display: flex;
  justify-content: space-between;
  font-size: 14px;
  color: #777;
  margin-top: 10px;
}

.post-detail-card {
  background: #fff;
  padding: 20px;
  border-radius: 12px;
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
  max-width: 700px;
  margin: 0 auto;
}

.back-button {
  background: #007bff;
  color: white;
  border: none;
  padding: 8px 14px;
  border-radius: 6px;
  cursor: pointer;
  margin-bottom: 20px;
}

.detail-footer {
  display: flex;
  justify-content: space-between;
  font-size: 14px;
  color: #777;
  margin-top: 10px;
}

তাহলে দেখব আমাদের পোস্ট গুলো সুন্দর কার্ড আকারে দেখাচ্ছে।

কোন কার্ডে ক্লিক করলে তা সম্পর্কে বিস্তারিত দেখাবে। হয়ে গেলো সুন্দর একটা রিয়েক্ট অ্যাপ! এই অ্যাপে একটা রিয়েক্ট অ্যাপের ব্যাসিক সব কিছুই রয়েছে। এর উপর ভিত্তি করে আমরা যে কোন কমপ্লেক্স অ্যাপ তৈরি করে ফেলতে পারব।

CSS কোড নিজে না লিখে বিভিন্ন CSS ফ্রেমওয়ার্ক ব্যবহার করতে পারি। Tailwind CSS, Material UI (MUI) ইত্যাদি।

Leave a Comment