ReactJS এ কিভাবে Redux ছাড়াই Redux এর মত করে reducer, dispatch, actions ব্যবহার করা যায়
Tags: react, context, dispatch, usereduce
Published: 2022-10-08 13:19:19

আজকে আমরা আলোচনা করব কিভাবে রিয়াক্ট এর Context API, useReducer, Custom Hooks দ্বারা Redux এর মতো `state` ম্যানেজ করতে পারি।  

এগুলো নিয়ে যারা অলরেডি জানেন বা গবেষণা করেছেন তারা এই পোস্টটি এড়িয়ে যেতে পারেন ।

কিভাবে Redux ছাড়াই  Redux এর মত করে reducer, dispatch, actions ব্যবহার করতে পারি সেটা দেখব. 

আমরা নিশ্চয় জানি রিয়েক্ট এর কোন ডাটা যদি চাইল্ড কম্পোনেন্ট কে পাস করতে চাই তাইলে আমরা ডাইরেক্ট নেস্টেট n তম চাইল কে আমরা সেই ডাটাটা পাঠাতে পারিনা, তার জন্য আমাদের সবগুলো ইন্টারমিডিয়েট চাইল্ডের ভেতর দিয়ে আমাদের সব ডাটা গুলো পাস করতে হয় এবং একসময় Last child component এ ডাটা এক্সেস করতে পারি Props আকারে।

মানে প্রতিটি চিল্ড্রেন কে প্রপস আকারে ডাটা পাঠাতে হবেই ।

প্যারেন্ট থেকে ডাইরেক্ট কোনো চাইল্ড কে কোনো state পাঠাতে পারব না । আর এই প্রবলেম থেকেই গ্লোবাল স্টেট বাবহার শুরু ।

সো. গ্লোবাল স্টেট কি?

গ্লোবাল স্টেট হচ্ছে আমাদের এপ্লিকেশন এর স্টেট যেটা  গ্লোবালি অ্যাকেসিবল হয়ে থাকে এবং যেটা 
এপ্লিকেশন এর ভিতরে যেকোনো কম্পোনেন্ট এক্সেস করতে পারে । যেমন Redux এর state।

আমরা কনটেক্স এপিআই দ্বারা আমাদের স্টেটকে যে কোন কম্পনেন্টে থেকে এক্সেস করতে পারি।

লেটেস্ট ভার্সন রিয়াক্ট ইউজ রিডিউসার নামে হোক তৈরি করেছে ইউজ রেডিওস ব্যবহার করে আমাদের লোকাল স্টেট কে আরো কমপ্লেক্স ভাবে ইউজ করতে পারি।

রিয়েক্ট ডিসপ্যাচ  দ্বারা স্টেট  কে পরিবর্তন করতে পারি

আর কন্টাক্সে এপিআই দ্বারা আমাদের অ্যাপ স্টেট কে যেকোন কম্পোনেন্ট বা নেস্টেড চাইল্ড কম্পোনেন্ট থেকেও accees করতে করতে পারি।

আচ্ছা প্রথমে আমরা React.createContext দ্বারা আমরা একটা কনটেক্স তৈরি করবো।

কোড অর্গানাইজেশন করার জন্য আলাদা ফাইল বানাবো।

AppContext.js

ইনিশিয়াল ভ্যালু হিসাবে আমরা নাল প্রোভাইড করি। আপনিই যেকোন ভ্যালু প্রোভাইড করতে পারেন।

```jsx
// AppContext.js
import {createContext} from "react";

export const AppContext = createContext(null)
```

  

এখন আমরা একটা কম্পনেন্ট তৈরি করব AppProvider.jsx 

.jsx কেনো ?  কারন context provider React component return করে। আর jsx রিটার্ন করে। তবে আপনি create রিয়েক্ট দিয়ে প্রোজেক্ট ইনিশিয়ালাইজ করলে extention js ইউজ করলেই হবে। আমি যেহেতু vite Js use korci তাই jsx ইউজ করছি ।

এর ভিতরে আমাদের এপ্লিকেশনের  `state` রাখবো। যেটা গ্লোবালী Accessible হবে। স্টেট create করার জন্য `useState` hook ব্যবহার না করে আমরা `useReducer` hook ইউজ করব। কারন useReducer স্টেট কে আরো অনেক ভাবে মডিফাই করতে পারব লাইক  `dispatch` করতে পারবো যে কোন অ্যাকশন টাইপ দ্বারা। 

এই কম্পনেন্ট দ্বারা আমাদের রুট এপ্লিকেশন কে wrap করে দেব । 

কারন আমরা এই কম্পনেন্ট এর ভেতরে আমরা context এর ডাটা Provide করব।

যেভাবে আমরা context প্রোভাইডার দ্বারা আমাদের এপ্লিকেশন রুট  কম্পনেন্ট কে wrap করে দেই।

`Example code:`

```jsx
// AppProvider.jsx
export let dispatch;

const initialState = {
   auth: null,
   posts: []
}

function AppProvider(props) {
    const [state, dispatch: dis] = useReducer(reducer, initialState)
		dispatch = dis;

    return (
        <AppContext.Provider value={state}>
            {props.children}
        </AppContext.Provider>
    )
}
```

এখন আমরা  `reducer` তৈরি করব।

যেটা `useReducer` ফার্স্ট প্যারামেটার আকারে গ্রহণ করে।

`reducer` হচ্ছে জাস্ট একটা ফাংশন যেটা রিটার্ন করে আমাদের আপডেট `state` কে।

`reducer` ফাংশন দুইটা প্যারামিটার গ্রহণ করে প্রথমটা state পরেরটা action অবজেক্ট । এই অবজেক্টটা পাব যখন dispatch মেথড কল করব তখন যে অবজেক্ট পাঠাবো।

অ্যাকশন অবজেক্ট ভিতরে `type` নামে আর একটা প্রোপার্টি থাকবে যেটার উপর বেস করে আমরা গ্লোবাল `state` কে পরিবর্তন করবো।

আমরা `switch` কেস ইউজ করব । আপনি ইচ্ছা করলে `if else` ব্লক ও ইউজ করতে পারেন। 

তবে শর্ত একটাই আপডেটেড `state` রিটার্ন করতে হবে।

`Example code:`

```jsx
// reducer.js
function reducer(state, action) {
    switch (action.type) {
        case "LOGIN":
            return {
                ...state,
                auth: action.payload,
            }

				case "FETCH_POSTS":
            return {
                ...state,
                posts: action.payload,
            }
        default:
            return state
    }
}
```

 

`AppProvider` দ্বারা আমাদের রুট অ্যাপ কে আমরা wrap করে দেবো ।

```jsx
// index.js
ReactDOM.createRoot(document.getElementById('root')).render(
     <React.StrictMode>
		    <AppProvider>
		        <App />
		    </AppProvider>
    </React.StrictMode>
)
```

ব্যাস আমাদের কাজ শেষ। 

এখন আমরা আমাদের `custom` `hook` তৈরি করব l যার সাহায্যে আমরা আমাদের গ্লোবাল কন্টেক্স `state` কে এক্সেস করতে পারব এবং `Dispatch` করতে পারব। যেভাবে Redux এর অ্যাকশন create করে।

যখন আমরা আমাদের এই `useStore` `custom` `hook`  ফাংশন কল করবো, তখন `Array` রিটার্ন পাব । যার প্রথম ইনডেক্সে এ থাকবে আমাদের কনটেক্স স্টেট এবং সেকেন্ড ইনডেক্সে এ থাকবে `useReducer` এর `dispatch` method.

`Example code:`

```jsx
import React, {useContext} from "react";
import AppContext, {dispatch}  from "./AppContext";

function useStore(){
    const state = useContext(AppContext)
    return [state, dispatch]

}
export default useStore
```

এবার আমরা ইউজ করব আমাদের কাস্টম `useStore` Hook ।

`App.jsx`

```jsx
// App.jsx
function App() {
    // useStore hook
    const [state, dispatch] = useStore()

    function handleLogin() {
        // after successfully api call
        dispatch({
            type: 'LOGIN',
            payload: user,
        })
    }
	
		console.log(state.auth) // it will update when context state change
	

    return (
        <div>
            <h1>App Component</h1>
						<button onClick={handleLogin}>Login</button>
        </div>
    )
}
export default App
```

এখানে হ্যান্ডেল লগইন যখন ফাংশন কল করলে `dispatch` method যেটা `useStore` রিটার্ন করছে এবং Dispatch method কল করার সময় প্যারামিটার হিসেবে `action` অবজেক্ট পাঠাব। যার একটা প্রোপার্টি `type` যেটার উপর বেস করে গ্লোবাল state কে পরিবর্তন করবে। এবং payload দিয়ে ডাটা পাশ করব।

পোস্ট পেজ কম্পোনেন্ট

```jsx
// Posts.jsx
function Posts() {
    // useStore hook
    const [{posts}, dispatch] = useStore()

    useEffect(()=>{
			fetch("/api/posts")
			.then(response=>response.json())	
			.then((data)=>{
				dispatch({
						type: "FETCH_POSTS",
						payload: data
					})
			})	
		}, [])
		

    return (
        <div>
            <h1>Posts</h1>
						{posts?.map(post=>(
							<li>{post.title}</li>						
						))}
        </div>
    )
}
export default Posts
```

কত সুন্দরভাবে আমরা `useReducer` ইউজ করতে পারি `Context API` এর সাথে। কোনো context manually ইম্পোর্ট না করেই।

লিখায় অনিচ্ছাকৃত ভূল ত্রুটি ক্ষমা সুন্দর দৃষ্টিতে দেখবেন। 🙏🏻 🙏🏻 🙏🏻