রিমিক্স হল একটি ফুলস্ট্যাক রিঅ্যাক্ট ফ্রেমওয়ার্ক বলতে কী বোঝায়, বিদ্যমান ওয়েব স্ট্যান্ডার্ডের উপর ফোকাস করে এবং ফ্রন্টএন্ডটিকে ব্যাকএন্ডের সাথে ঘনিষ্ঠভাবে বেঁধে রাখে। যখন আপনি দেখেন যে আপনার প্রতিক্রিয়া উপাদানগুলিতে ডেটা লোড করা বা ফর্ম থেকে জমা দেওয়া ডেটা কীভাবে প্রক্রিয়া করা যায় তা দেখে এই টাইট কাপলিংটি তাজা বাতাসের একটি শ্বাস।
এই নিবন্ধে আমরা ডাটাবেস হিসাবে Upstash Redis ব্যবহার করে একটি সাধারণ ফিচার ফ্ল্যাগ ম্যানেজমেন্ট সিস্টেম তৈরি করে রিমিক্সের শক্তি দেখতে পাব।
সম্পূর্ণ সোর্স কোড এখানে পাওয়া যাবে।
সেটআপ
৷
আপনি npx create-remix@latest
চালিয়ে একটি একেবারে নতুন রিমিক্স অ্যাপ পাবেন এবং আপনার পছন্দের স্থাপনার পরিবেশ বেছে নিন। আমি ভার্সেলের সাথে গিয়েছিলাম কিন্তু এই টিউটোরিয়ালের জন্য এটি কোনও পার্থক্য করবে না৷
Upstash Redis-এর সাথে আপনি সংযোগ করতে পারেন এমন দুটি উপায় রয়েছে:প্রথমটি হল একটি TCP সংযোগের মাধ্যমে যেখানে আপনি অভ্যস্ত যেকোন মানক Redis ক্লায়েন্ট লাইব্রেরি ব্যবহার করতে পারেন। দ্বিতীয়টি Upstash এর REST API এর মাধ্যমে। আমরা দ্বিতীয় বিকল্পটি নিয়ে যাব কারণ এটি সমস্ত সার্ভারহীন পরিবেশে উপলব্ধ, যেমন ক্লাউডফ্লেয়ার ওয়ার্কার্স পরিবেশ যেখানে রিমিক্স স্থাপন করা যেতে পারে। Upstash এর একটি প্যাকেজ রয়েছে যা প্রকৃত Redis কমান্ডের অনুকরণ করে, কোন ফাংশনকে কল করতে হবে তা জানা সহজ করে তোলে।
আমাদের এখন আমাদের Upstash Redis ডাটাবেসের সাথে সংযোগ করার জন্য প্রয়োজনীয় দুটি পরিবেশ ভেরিয়েবল সংরক্ষণ করার একটি উপায় প্রয়োজন। রিমিক্স বাক্সের বাইরে ডেভেলপমেন্ট env var সমর্থনের সাথে আসে না, তবে এটি একটি বিকাশ নির্ভরতা হিসাবে ডোটেনভ যোগ করে সম্পন্ন করা যেতে পারে।
npm add --save-dev dotenv
আমাদের .env
-এ ফাইল (যা .gitignore
এ যোগ করা উচিত ) আমরা Upstash Redis এর সাথে সংযোগ করার জন্য প্রয়োজনীয় দুটি env var সেট আপ করতে পারি। @upstash/redis
প্যাকেজ স্বয়ংক্রিয়ভাবে এইগুলি সনাক্ত করে তাই আমাদের কোডের মধ্যে সংযোগ করার প্রয়োজন নেই। একটি নতুন রেডিস ডাটাবেস তৈরি করার পরে এই মানগুলি Upstash ড্যাশবোর্ডের মধ্যে পাওয়া যাবে৷
UPSTASH_REDIS_REST_URL="https://..."
UPSTASH_REDIS_REST_TOKEN="..."
আমাদের dev
আপডেট করতে হবে স্ক্রিপ্ট dotenv আছে env vars পিক আপ. অন্যান্য স্ক্রিপ্ট একই থাকতে পারে।
{
"scripts": {
"dev": "node -r dotenv/config node_modules/.bin/remix dev"
}
}
ফিচার ডেটা সংরক্ষণ করা
৷বৈশিষ্ট্য ফ্ল্যাগগুলি অবিশ্বাস্যভাবে জটিল হতে পারে, আপনার ব্যবহারকারীর শতাংশের রোলআউট প্ল্যানের সাথে, ব্যবহারকারীদের নির্দিষ্ট গোষ্ঠীর জন্য সক্ষম, তবে সেগুলি "চালু" এবং "অফ" এর মতো সহজও হতে পারে। আমরা Redis প্রদান করে হ্যাশ ডেটা টাইপ ব্যবহার করে আমাদের বৈশিষ্ট্য পতাকা সংরক্ষণ করব। আমাদের ডেটা নীচের JSON-এর মতো দেখাবে, যেখানে "1" সক্রিয়/চালু এবং "0" নিষ্ক্রিয়/বন্ধ।
{
"chart": "1",
"graph": "0"
}
এই ডেটা অ্যাক্সেস এবং ম্যানিপুলেট করতে আমরা Redis দ্বারা প্রদত্ত চারটি কমান্ড/ফাংশন ব্যবহার করব:
- সকল কী (বৈশিষ্ট্য) এবং মান (সক্ষম/অক্ষম) পুনরুদ্ধার করতে hgetall।
- একটি নির্দিষ্ট বৈশিষ্ট্য পতাকা সক্ষম বা নিষ্ক্রিয় করতে hset৷
- একটি নির্দিষ্ট বৈশিষ্ট্য পতাকা মুছে ফেলার জন্য hdel।
- একবারে একাধিক কিন্তু নির্দিষ্ট বৈশিষ্ট্যের পতাকা মান পেতে hmget।
বৈশিষ্ট্যগুলি পরিচালনা করা
৷
আমরা /features
-এ অবস্থিত একটি পৃষ্ঠা তৈরি করব যা বিদ্যমান বৈশিষ্ট্যগুলি তৈরি এবং পরিচালনার (সক্ষম/অক্ষম/মুছুন) দায়িত্বে রয়েছে৷ আমরা কি AddFeature
সম্পর্কে বিস্তারিত জানাব এবং FeatureList
যখন আমরা আলোচনা করি কিভাবে ডাটা লোড করতে হয় এবং তারপর কিভাবে ডাটা লিখতে হয়।
// app/routes/features.tsx
export default function Features() {
return (
<div>
<h1>Features</h1>
<AddFeature />
<FeatureList />
</div>
);
}
ডেটা লোডার
একটি ডেটা লোডার হল রিমিক্সে loader
নামে একটি এক্সপোর্ট করা ফাংশন যা সার্ভারে চালিত হয় এবং একটি হুকের মাধ্যমে আমাদের প্রতিক্রিয়া উপাদানে উপলব্ধ ডেটা প্রদান করে৷
আমরা বৈশিষ্ট্য পতাকা তৈরি এবং পরিচালনা করার জন্য একটি পৃষ্ঠা দিয়ে শুরু করছি, এবং এই ক্ষেত্রে আমরা সমস্ত বৈশিষ্ট্য ফিরিয়ে দিতে চাই। এগুলি জোড়ার অ্যারে হিসাবে ফেরত দেওয়া হবে:
[
["graph", true],
["chart", false]
]
একটি TypeScript প্রকার সংজ্ঞা দিয়ে শুরু করে, আমরা তারপর loadAllFeatures
নামে একটি ফাংশন দেখতে পাব। যেটি hgetall
ব্যবহার করে @upstash/redis
থেকে ফাংশন .
import { Redis } from "@upstash/redis";
type LoaderData = {
features: Array<[string, boolean]>;
};
const loadAllFeatures = async () => {
const redis = Redis.fromEnv();
const data = await redis.hgetall("features");
const features: Array<[string, boolean]> = [];
for (let i = 0; i < data.length; i += 2) {
features.push([data[i], data[i + 1] === "1"]);
}
return features.sort((a, b) => {
if (a[0] > b[0]) return 1;
if (a[0] < b[0]) return -1;
return 0;
});
};
রপ্তানি করা loader
ফাংশন নিজেই loadAllFeatures
কল করবে ফাংশন, রিঅ্যাক্ট কম্পোনেন্টে পাস করা বৈশিষ্ট্যগুলি ফিরিয়ে দেওয়া।
export const loader: LoaderFunction = async (): Promise<LoaderData> => {
// You would want to add authentication/authorization here
const features = await loadAllFeatures();
return { features };
};
আমরা এই প্রতিক্রিয়া উপাদানটির বিশদ বিবরণ পরে কভার করব, তবে আপনি কীভাবে লোডার ফাংশন থেকে ফিরে আসা ডেটা অ্যাক্সেস করেন তা দেখানোর জন্য, আপনি useLoaderData
নামে একটি রিমিক্স হুক ব্যবহার করেন .
const FeatureList = () => {
const { features } = useLoaderData<LoaderData>();
return (
<ul>
{features.map(([feature, active]) => (
<li key={feature}>{/* coming soon */}</li>
))}
</ul>
);
};
ফর্ম অ্যাকশন
আমরা দেখেছি কিভাবে ডেটা লোড হয়, কিন্তু এই পর্যায়ে আমাদের ফিচার ফ্ল্যাগ ডাটাবেসে আসলে কোনো ফিচার নেই! ফর্ম অ্যাকশন খেলায় আসে যেখানে এখানে. action
নামে একটি ফাংশন রপ্তানি করে রিমিক্সে ডেটা প্রক্রিয়া করা হয় . অনেকটা loader
এর মত , এটি সার্ভারে চালানো হয় এবং এটি সাধারণত json
প্রদান করে প্রতিক্রিয়া উপাদান অন্য হুকের মাধ্যমে অ্যাক্সেস করতে পারে এমন ডেটা, অথবা এটি ব্রাউজারকে redirect
করতে বলতে পারে অন্য পৃষ্ঠায়।
action
নীচের ফাংশন আসলে চারটি ভিন্ন ধরণের ক্রিয়া পরিচালনা করে, একটি বৈশিষ্ট্য তৈরি করে, একটি বৈশিষ্ট্য সক্রিয়/অক্ষম করে এবং একটি বৈশিষ্ট্য মুছে দেয়। আমরা এটি একটি switch
দিয়ে পরিচালনা করি বিবৃতি যা তারপর উপযুক্ত রেডিস ফাংশন/কমান্ডকে কল করে।
export const action: ActionFunction = async ({ request }) => {
// You would want to add authentication/authorization here
const formData = await request.formData();
const feature = formData.get("feature") as string;
const action = formData.get("_action") as string;
if (!feature || feature.length === 0) {
// This isn't currently displayed in our component
return json({ error: "Please provide a feature" });
}
switch (action) {
case "create":
case "enable":
await redis.hset("features", { [feature]: 1 });
break;
case "disable":
await redis.hset("features", { [feature]: 0 });
break;
case "delete":
await redis.hdel("features", feature);
break;
}
return redirect("/features");
};
আপনি লক্ষ্য করবেন যে সফলতার সাথে, আমি সেই পৃষ্ঠায় পুনঃনির্দেশ করি যে ব্যবহারকারী বর্তমানে আছেন। এটি মূলত loader
কল করে একটি পৃষ্ঠা পুনরায় লোড ট্রিগার করে ফাংশন এবং ব্যবহারকারীর কাছে যা প্রদর্শিত হয় তা আপডেট করা।
একটি নতুন বৈশিষ্ট্য পতাকা যোগ করতে, AddFeature
কম্পোনেন্ট রিমিক্স Form
ব্যবহার করবে উপাদান যা আমরা উপরে দেখেছি অ্যাকশন ফাংশনে ডেটা জমা দেবে। আমি উল্লেখ করেছি যে এটি post
এর মাধ্যমে জমা দেওয়া উচিত পদ্ধতি এবং replace
প্রদান করেছে প্রপ যাতে আমরা যখনই একটি বৈশিষ্ট্য পতাকা তৈরি করি তখন এটি ব্রাউজারের ইতিহাসে একটি নতুন পৃষ্ঠা যোগ না করে৷
const AddFeature = () => {
return (
<Form method="post" replace>
<input type="hidden" name="_action" value="create" />
<input type="text" name="feature" required placeholder="name" />
<button type="submit">Add</button>
</Form>
);
};
একবার একটি বৈশিষ্ট্য তৈরি হয়ে গেলে, আমরা সমস্ত বর্তমান বৈশিষ্ট্য ফ্ল্যাগগুলি দেখাতে চাই যাতে সেগুলি পরিচালনা করা যায়৷ প্রতিটি বৈশিষ্ট্য পতাকা আসলে দুটি ফর্ম প্রদর্শন করে:একটি বৈশিষ্ট্য পতাকা সক্ষম/অক্ষম করার জন্য এবং দ্বিতীয়টি মুছে ফেলার জন্য৷
মনে রাখবেন যে দুটি লুকানো ক্ষেত্র আছে:_action
যাতে আমাদের action
ফাংশন জানে যে আমরা বৈশিষ্ট্যটিতে কী করার চেষ্টা করছি এবং feature
যেটি পতাকার নাম পাঠায় যা আমরা পরিবর্তন করতে চাই।
const FeatureList = () => {
const { features } = useLoaderData<LoaderData>();
return (
<ul>
{features.map(([feature, active]) => (
<li key={feature}>
<Form method="post" replace>
<input
type="hidden"
name="_action"
value={active ? "disable" : "enable"}
/>
<input type="hidden" name="feature" value={feature} />
<button type="submit" className="btn-naked">
{active ? "💪" : "🦾"}
</button>
</Form>
<span>{feature}</span>
<Form method="post" replace>
<input type="hidden" name="_action" value="delete" />
<input type="hidden" name="feature" value={feature} />
<button type="submit">Delete</button>
</Form>
</li>
))}
</ul>
);
};
বৈশিষ্ট্যগুলি ব্যবহার করা
৷
আমাদের আপস্ট্যাশ রেডিস ডাটাবেসে বৈশিষ্ট্যযুক্ত পতাকা রয়েছে, তবে কী ভাল যে আমরা যদি এই ফ্ল্যাগের উপর ভিত্তি করে আমাদের অ্যাপে কার্যকারিতা চালু বা বন্ধ না করি। hmget
ব্যবহার করে ডাটাবেস থেকে নির্দিষ্ট বৈশিষ্ট্য লোড করতে আমরা একটি লোডার ফাংশন ব্যবহার করব , এবং তারপরে সঠিক কাঠামোতে এটি পেতে একটু ডেটা ম্যানিপুলেশন।
আমরা যদি ["chart", "graph", "fake"]
লোড করতে চাই পতাকা, Redis আমাদের ["1", "0", null]
ফেরত দেবে ... মনে রাখবেন যে পতাকা বিদ্যমান না থাকলে, এর মান হবে null
, যা আমি fake
অন্তর্ভুক্ত করে দেখাতে চেয়েছিলাম পতাকা।
type LoaderData = {
features: Record<string, boolean>;
};
const loadFeatures = async (keys: Array<string>) => {
const data = await redis.hmget("features", ...keys);
const features = keys.reduce<Record<string, boolean>>((acc, key, index) => {
acc[key] = data[index] === "1";
return acc;
}, {});
return features;
};
export const loader: LoaderFunction = async (): Promise<LoaderData> => {
const features = await loadFeatures(["chart", "graph"]);
return { features };
};
আমরা এখন আবার রিমিক্সের useLoaderData
ব্যবহার করে আমাদের কম্পোনেন্টে লোড করা ডেটা অ্যাক্সেস করতে পারি হুক তারপর একটি পতাকা বর্তমানে সক্ষম বা নিষ্ক্রিয় কিনা তা বিবেচনা করে আমাদের ওয়েবসাইটের কার্যকারিতা কীভাবে পরিবর্তিত হবে তা চয়ন করুন৷
export default function Index() {
const { features } = useLoaderData<LoaderData>();
return (
<div>
<h1>Dashboard</h1>
{features.chart ? <h2>Chart</h2> : <h2>No Chart</h2>}
{features.graph ? <h2>Graph</h2> : <h2>No Graph</h2>}
</div>
);
}
উপসংহার
এই নিবন্ধে আমরা দেখেছি কিভাবে আপস্ট্যাশ রেডিস ব্যবহার করে রিমিক্সে একটি সাধারণ ফিচার ফ্ল্যাগ সিস্টেম তৈরি করতে হয়, এর ডেটা লোডার এবং ফর্ম অ্যাকশন সার্ভার সাইড ফাংশনের সুবিধা নিয়ে। এগুলি আমাদের একটি নির্দিষ্ট পৃষ্ঠার ব্যাকএন্ড এবং ফ্রন্টএন্ডকে শক্তভাবে সংযুক্ত রাখতে দেয়, একটি পৃথক GraphQL API সেট আপ করার প্রয়োজন ছাড়াই দ্রুত পুনরাবৃত্তি করে এবং ফ্রন্টএন্ডে স্ট্যান্ডার্ড ফর্ম জমা দেওয়ার ঘটনাগুলিকে ওভাররাইড করে৷ ফর্মগুলি কীভাবে তাদের ডেটা জমা দেয় সে সম্পর্কে আমরা ওয়েব স্ট্যান্ডার্ডের দিকে ঝুঁকতে দেখেছি বলে রিমিক্স৷