React+Redux (রিয়েক্ট+ রিডাক্স) Part-4

Jahangir Alam
17 min readJan 8, 2021

Full react series is from udemy course Modern React with Redux by Stephen grider

Content

  • 18. Redux Dev tools
  • 19. Handling forms with Redux form
  • 20. REST based React app

18. Redux Dev tools

এখন প্রথমে আমরা redux-devtool-extension ইন্সটল করবো আমাদের ক্রম ব্রাউজারে ।

উপরের এই লিঙ্কে গেলে সেখান থেকে chrome এর জন্য এক্সটেনশন খুঁজে বেরকরে ইন্সটল করবো ।

এরপর কোডে এটা ইন্টিগ্রেট করে দিবো ।

এখানে সবুজ লেখাটি src/index.js ফাইলের কোডে ইন্টিগ্রেট করে দিবো ।

আগে শুধু createStore ছিল এখন applyMiddleware এবং compose ইম্পরট করে redux dev tool ইন্টিগ্রেট করে দিলাম ।

এখন ব্রাউজারে গিয়ে redux dev tool এর আইকনে ক্লিক করলে এই উইন্ডো ওপেন হবে । এখান থেকে স্টেট এর অবস্তা দেখা যাবে ।

এখানে বাম পাশের নিচের দিকে ৩ টা বাটন আছে সেখান থেকে মাঝের বাটনে ক্লিক করলে এই উইন্ড টা আলাদা হয়ে চলে আসবে, তখন এটা আলাদা ভাবে ব্যাবহার করা যাবে ।

এছাড়াও অন্য কোনও সাইটে গেলে এখান থেকে উপরের ড্রপ ডাউন থেকে সাইট সিলেক্ট করলে ঐ সাইটের ইনফরমেশন দেখাবে ।

Debug session with Redux Dev tools:

localhost:3000?debug_session=<some_string>

Saves all data in redux store between refreshes of the page.

উপরের মতো করে এখানে debug_session এর ভেলু যেকোনো কিছু দেওয়া যাবে এটা দিলে আমরা debugging করতে পারবো । দুইটা রিফ্রেশের মাঝে সকল কিছু ই সংরক্ষণ করে রাখবে ।

19. Handling forms with Redux form

যখন আমরা redux ছারা শুধু react ব্যাবহার করেছি তখন এই ফর্ম হেন্ডেল করার জন্য এই প্রসেস ফলো করেছি , তখন dom থেকে কোন ডাটা আসলে সেটাকে class component এর স্টেটে সেভ করে রাখতাম কিন্তু redux এ সামান্য একটু ভিন্ন ভাবে কাজ করে ,

এখানে ডাটা সেভ করে রাখে redux store এ । DOM থেকে ডাটা আসলে সেটাকে কম্পোনেন্ট থেকে কোনও মেথডের মাধ্যমে action creator এ কল করে এরপর reducer এর মাধ্যমে স্টোরে সেভ হয় আবার ডাটা এক্সেস করতে হলে mapStateToProps এর মাধ্যমে এক্সেস করতে হয় ।

কিন্তু এখানে redux-form এর সুবিধাটা হচ্ছে action creator এবং reducer এর কাজ করতে হয় না , redux-form অটোম্যাটিক ভাবে এগুলো হেন্ডেল করে । আমাদেরকে শুধু component এ props এবং handler এর কাজ করতে হয় । এখন redux-form ইন্সটল করি ।

npm install --save redux-form

redux-form এর ডকুমেন্টেশন এখানে নিচের লিঙ্কে ।

এখান থেকে example এ যেয়ে এরপর wizard form এ যাবো, এটা আসলে অনেক প্রজেক্টে ব্যাবহার করা হয় । আমরা এখন এখান থেকে Synchronous validation এক্সাম্পল ব্যাবহার করবো ।

connecting redux from

redux form কে প্রজেক্টে integrate করতে হলে প্রথমে reducers/index.js ফাইলে নিচের লাইনটি দিয়ে ইম্পরট করে নিতে হবে ।

import { reducer as formReducer } from 'redux-form'

এরপর combineReducers এ form: formReducer সেট করে দিবো । এখন redux dev tool ওপেন করলে

দেখা যাবে যে form নামে একটি স্টেট তৈরি হয়েছে ।

এখন StreamCreate.js ফাইলে নিচের কোড গুলো লিখি।

এখানে import সেকশনে Field হচ্ছে কম্পোনেন্ট আর reduxForm হচ্ছে একটি ফাংশন , এটার কাজ অনেকটা redux এর connect()() ফাংশনের মতো , এটা প্রথমে একটি ফাংশন রিটার্ন করে এরপর আবার রিটার্ন এ একটি ফাংশন কল হয় ।

render এ Field কম্পোনেন্টের ভেতর renderInput মেথডের রেফারেন্স পাস করে দিয়েছি আর lable এ যা দিয়েছি সেটা এই মেথডের ভেতর props হিশেবে পাস হয়ে যাবে । সেটা renderInput এর ভেতর input এবং label কে destructuring করে নিয়েছি ।

এখন render() এর উপরে নিচের কোড গুলো লিখি

onSubmit(event) {
event.preventDefault();
}

এরপর render() এর ভেতরে props কে কনসল লগ করলে নিচের মতো অনেক কয়টা মেথড দিবে , এগুলো মূলত redux দেয় ।

console.log(this.props);

এগুলোর মধ্যে একটি হচ্ছে handleSubmit() মেথড ।

Handling form submission

এখন এই onSubmit() মেথড থেকে props হিশেবে event টা তুলে ফেলি এবং preventDefault টাও ডিলিট করে দেই কারণ এটা redux অটোম্যাটিক টেক কেয়ার করে । এরপরিবরতে ফর্ম সাবমিট করার পর যে ভেলু নিবে সেটা নেই ।

onSubmit(formValues) {
console.log(formValues)
}

এরপর <form> ট্যাগের ভেতর নিচের কোড লিখি ।

onSubmit={this.props.handleSubmit(this.onSubmit)}

এখানে handleSubmit এর ভেতর onSubmit মেথড এর রেফারেন্স দিয়ে দেই এবং নিচে একটি বাটন দিয়ে দেই ।

<button className='ui button primary'>Submit</button>

এই বাটনে ক্লিক করলে নিচের মতো আউটপুট দিবে ।

এখানে কোনও কিছু দেওয়ার পর Submit বাটনে ক্লিক করলে কনসলে সেই ভেলুগুলো দেখাবে ।

Validation of form input:

form validation এর জন্য redux form অটোম্যাটিক ভাবে validate মেথড টি কল করে ।

validate method থেকে যদি কোন এরর না আসে তাহলে empty অবজেক্ট রিটার্ন করে অন্যথায় error মেসেজ রিটার্ন করে ।

এখন এই মেথডটি export default এর উপরে ডিক্লেয়ার করবো ।

এখানকার formValues এর ভেতরে সকল ফর্ম ভেলু থাকবে । formValues.title এবং <Field> এর title এক হলে এবং এখানে যদি কোনও অবজেক্ট রিটার্ন করে তাহলে renderInput এর ভেতর props হিশেবে পাঠিয়ে দেয় redux-form . এখন এই validate কে reduxForm এর ভেতর পাঠিয়ে দেই যাতে redux-form এটাকে চিন্তে পারে ।

export default reduxForm({
form: "streamCreate",
validate
})(StreamCreate);

error message পেলে redux-form অটোম্যাটিক re-render করে ।

<Field> এর name property র সাথেই এটার মুল কানেকশন । name property এবং এখানকার object এর প্রপার্টি মিল পেলেই এটা কাজ করে । এখন এই এরর প্রপার্টি এক্সেস পাবো আমরা meta অবজেক্টের মাধ্যমে । কনসলে এই meta object লগ আউট করলে অনেক প্রপার্টি এবং ফাংশন পাবো । এখান থেকে error এবং touched এই দুটো মেথড ই কাজে লাগবে ।

renderInput এ meta কে destructure করে নিয়ে এর নিচে console.log করলে নিচের মতো দেখা যাবে ।

console.log(meta)

showing errors on touch:

renderError() মেথডে meta পাস করে দেওয়া হয় , আর এই মেথড renderInput থেকে কল করা হয় । এই meta অবজেক্টে অনেক কয়টা প্রপার্টি এবং মেথড আছে, এরমধ্যে দুটি হচ্ছে error এবং touched । renderError() মেথডে এই দুইটা প্রপার্টি extract করে নিয়েছি । এরপরের কন্ডিশনে touched এবং error এর ভেলু true থাকতে হবে হবে তাহলে error দেখাবে , এর মানে হচ্ছে ইনপুট ফিল্ডে একবার টাচ করা হয়েছে এবং এখানে কোনও ভেলু দেওয়া হয় নাই, তাহলেই কেবল এরর দেখাবে ।

এরপরে renderInput এ তিনটা প্রপার্টি extract করেছি , input — input value, label- এটা <Field> এর ভেতর পাস করে দেওয়া হয়েছে । আর শেষেরটা meta, এই ভেলুটা আসছে মূলত validate থেকে । এটা অটোম্যাটিক ভাবে renderInput() মেথডের ভেতর পাস হয়ে যায় ।

const className = `field ${meta.error && meta.touched ? 'error': ''}`;

এই কন্ডিশন দিয়ে ক্লাস নেম ডাইনামিক করে দেওয়া হয়েছে । দুইটা কন্ডিশন ট্রু হলে error ক্লাস বসবে ।

20. REST based React app

এখানে আমরা বেকেন্ড সার্ভারে রিকোয়েস্ট করবো react থেকে , back-end আমরা এক্সপ্রেস দিয়ে ডিজাইন করবো ।

যেকোনো আইডিতে রিকোয়েস্ট করলে সার্ভার থেকে ডাটা নিয়ে এসে দেখাবে ।

REST api এর কিছু কনভেনশন আছে, এটা হচ্ছে কিছু route থাকবে, ঐ route এ রিকোয়েস্ট করার জন্য কিছু মেথড আছে যেমন GET, POST,PUT, DELETE এগুলোর মাধ্যমে রিকোয়েস্ট করলে এর রিলেটেডে কিছু মেথড ডিফাইন করা থাকবে যে কোন রিকোয়েস্ট আসলে কি করতে হবে ।

এখন নিচের কমান্ডগুলো দিয়ে নতুন একটি api সার্ভার তৈরি করি ।

cd ..
mkdir api
cd api
npm init -y

এখন ফেক ডাটা আনার জন্য আমরা json-server নামে একটি npm লাইব্রেরি ব্যাবহার করব । এখন নিচের কমান্ড দিয়ে json-server ইন্সটল করি ।

npm i json-server

উপরের এই কমান্ড দিয়ে json-server ইন্সটল করি ।

এখন api ডিরেক্টরির আন্ডারে db.json নামে একটি ফাইল তৈরি করে নিচের লাইনটি লিখি।

{
"streams": []
}

json-server ডকুমেন্টেশন অনুযায়ী এই ফাইল টি তৈরি করতে হবে । এই ফাইলটা আসলে ডাটাবেজের মতো কাজ করবে REST convension অনুযায়ী ।

এখন package.json ফাইলে নিচের কোডগুলো লিখি ।

"scripts": {
"start": "json-server -p 3001 -w db.json"
},

এখন টার্মিনাল ওপেন করে npm start দিলে নিচের মতো সিম্পল একটি json সার্ভার ওপেন হবে ।

এখানে দেখাচ্ছে যে

http://localhost:3001/streams

এই লিঙ্ক থেকে ডাটা আসবে । আর db.json ফাইলে কোন পরিবর্তন আসলে এখানে রিফ্লেক্ট করবে ।

এখন api এবং client এ গিয়ে দুইটাতেই প্রজেক্ট রান করবো npm start দিয়ে। এরপর onSubmit() মেথডের ভেতর axios এর মাধ্যমে নেটওয়ার্ক রিকোয়েস্ট করবো ।

এখন client প্রজেক্টে নিচের কমান্ডের মাধ্যমে axios, redux-thunk ইন্সটল করি ।

npm install --save axios redux-thunk

এই দুইটা প্যাকেজ সাধারণত লাগে asynchronous action creator এর জন্য ।

Creating a stream with Action creator and REST convension:

api project এর url এ রিকোয়েস্ট করার জন্য একটি client প্রজেক্টের action crator এর মাধ্যমে কল করতে হবে । সেজন্য client/src/ এই ডিরেক্টরির আন্ডারে api নামে একটি ডিরেক্টরি তৈরি করে সেখানে streams.js নামে একটি ফাইল তৈরি করে নিচের কোড গুলো লিখি ।

import axios from 'axios';    export default axios.create({
baseURL: 'http://localhost:3001'
})

এখন actions/index.js ফাইলে এই ফাইলটি import করে একটি action creator তৈরি করবো ।

export const createStream = formValues => async dispatch => {
streams.post('/streams', formValues)
}

এটা একটি async process তাই redux-thunk এর জন্য dispatch ব্যাবহার করেছি । এটা প্রথমে formValues নামে একটি প্যারামিটার নেয় এরপর রিটার্ন হিশেবে একটি ফাংশন কল করে, এই ফাংশনটিও dispatch নামে একটি প্যারামিটার নেয় , এটা একটি async প্রসেস । এখানে আমরা জাস্ট একটি একশন ক্রিয়েটর তৈরি করেছে যেটা জাস্ট একটি পোস্ট রিকোয়েস্ট করে , আমরা ডাটা আসার জন্য অপেক্ষা করি নাই অর্থাৎ await ব্যাবহার করি নাই এবং reducer ও ব্যাবহার করি নাই ।

এখন streamCreate.js ফাইলে connect এবং createStream দুটি import করি ।

import { connect } from 'react-redux'
import { createStream } from '../../actions'

এরপর createStream নিচের মতো করে দিয়ে দিবো connect মেথডের ভেতর ।

const formWrapped = reduxForm({
form: "streamCreate",
validate
})(StreamCreate);
export default connect(null, { createStream })(formWrapped);

এখন ইউজার যখন submit button এ ক্লিক করবে তখন onSubmit মেথডে কল হবে , এখন এই onSubmit() মেথডে createStream() action creator কে কল করবো ।

onSubmit = formValues => {
this.props.createStream(formValues)
}

এখানে onSubmit() মেথড কে arrow function এ কনভার্ট করেছি কারণ এটা না করলে props থেকে createStream() মেথড কে পাবে নাহ তাই this কে binding করে দিবো ।

এখন redux-thunk কে wire up করে দিবো । src/index.js ফাইলে নিচের কোড গুলো লিখবো ।

import reduxThunk from 'redux-thunk'const store = createStore(
reducers,
composeEnhancers(applyMiddleware(reduxThunk))
);

এখানে redux-thunk টা জাস্ট ইম্পরট করে applyMiddleware() এর ভেতর পাস করে দিয়েছি ।

এখন ফর্মে ডাটা দিয়ে submit দিলে সেই ডাটাটা db.json ফাইলে যেয়ে জমা হবে ।

Dispatching action after stream creation:

এখানে মূলত actions/index.js ফাইলের নিচের এই লাইন থেকে front end থেকে back end এ ডাটা পাস হয় ।

streams.post('/streams', formValues)

এখন আমরা এখান থেকে ডাটা টাকে redux store এ সেভ করবো dispatch এর মাধ্যমে অর্থাৎ action creator থেকে dispatch করবো ।

actions/types.js ফাইলে action creator এ টাইপ টা একটি ভেরিয়েবলে রাখি ।

export const CREATE_STREAM = 'CREATE_STREAM'

এরপর actions/index.js ফাইলে নিচের কোড গুলো লিখে response এর ডাটা টাকে dispatch করে দেই ।

import { SIGN_IN, SIGN_OUT, CREATE_STREAM } from './types'export const createStream = formValues => async dispatch => {
const response = streams.post('/streams', formValues)
dispatch({ type: CREATE_STREAM, payload: response.data })
}

Bulk Action creator

এখানে যতগুলো action আছে সব গুলো এখন তৈরি করবো । প্রথমে types.js ফাইলে আগে type গুলো দেই ।

export const FETCH_STREAMS = 'FETCH_STREAMS'
export const FETCH_STREAM = 'FETCH_STREAM'
export const DELETE_STREAM = 'DELETE_STREAM'
export const EDIT_STREAM = 'EDIT_STREAM'

এরপর actions/index.js ফাইলে import করে সকল action creator তৈরি করি ।

Object-based reducers

আগে আমরা যত গুলো করেছি সবগুলোতে array কে manipulation করেছি কিন্তু এখন আমরা object কে manipulation করবো ।

এখানে আমরা array based approach এবং object based approach দুইটাই দেখিয়েছি । array দিয়ে করতে গেলে অনেক কোড লিখতে হয় কিন্তু অবজেক্টে অল্প কোড দিয়েই করা যায় জাস্ট ১ লাইনেই হয়ে যায় ।

return { ...state, [action.payload.id]: action.payload }

এখানে [action.payload.id] দিয়ে বোঝানো হচ্ছে যে এটা একটা array না, এটা হচ্ছে key interpolation syntax , এটা একটা ডাইনামিক ভাবে key নিবে । এরপর ঐ key এর ভেলু হিশেবে action.payload দিয়ে দিবে ।

এখন এটা আমরা আমাদের প্রজেক্টে ইমপ্লিমেন্ট করবো ।

npm i lodash

Handeling Fetching, Creating Updating, Deleting and Merging List of record :

Merging Lists of Records :

backend থেকে অবজেক্টের array হিশেবে ডাটা আসবে এরপর সেই ডাটার id কে key হিশেবে নিবো আর পুরা অবজেক্ট কে এর ভেলু হিশেবে নিবো ।

case FETCH_STREAMS:
return { ...state, ..._.mapKeys(action.payload, 'id') }

এরপর reducer ডিরেক্টরির index.js ফাইলে streamReducer ফাইলটি ইম্পরট করে combineReducers মেথডের ভেতর নিচের অবজেক্টটি দিয়ে দেই ।

streams: streamReducer

এখন StreamList ফাইলে নিচের কোড গুলো লিখে redux store এর সাথে ইন্টিগ্রেট করে দেই ।

এখানে প্রথমে redux store এর সাথে কানেক্ট করার জন্য connect মেথড ইম্পরট করে নিয়েছি এরপর fetchStreams একশন ক্রিয়েটর ইম্পরট করেছি ।

আগে এখানে ফাংশন বেজ কম্পোনেন্ট ছিল এখন এটাকে ক্লাস বেজ কম্পোনেন্টে কনভার্ট করেছি কারণ componentDidMount() মেথডের ভেতর আগেই api এর মাধ্যমে stream এর list আনবো । শেষে connect মেথডের ভেতর এই action creator পাস করে দিয়েছি । এখানে আমরা এখনো mapStateToProps() কল করিনি কারণ জাস্ট দেখতে চাচ্ছি যে ডাটা redux store এ এসেছে কিনা ।

Render All stream:

redux store এ ডাটা গুলো আছে অবজেক্ট ফর্মে । আমরা এগুলোকে mapStateToProps() এ array তে কনভার্ট করবো কারণ তখন ডাটা গুলোকে map() মেথডের ভেতর পাঠিয়ে লিস্ট তৈরি করতে পারবো । আমরা অবশ্য lodash লাইব্রেরি ব্যাবহার করে object এর জন্য map এর কাজ করতে পারবো কিন্তু জাস্ট সিমপ্লিসিটির জন্য এগুলোকে array তে কনভার্ট করবো ।

const mapStateToProps = (state) => {
return { streams: Object.values(state.streams) };
};

Object.values() মেথডের ভেতর অবজেক্ট পাঠালে সেটাকে array তে কনভার্ট করে দেয় । এখন নিচের কোড গুলো লিখে এই array element দিয়ে লিস্ট রেন্ডার করবো ।

এখানকার অবজেক্ট গুলোকে array তে কনভার্ট করে লিস্টে দেখিয়েছি ।

getState()

এখন আমরা চাচ্ছি যে লিস্টের পাশে delete, edit বাটন রাখতে , যদি ইউজার অথেনটিক হয় তাহলে এই বাটন গুলো দেখতে পারবে এবং delete, edit করতে পারবে কিন্তু যদি না হয় জাস্ট শুধু লিস্ট দেখতে পারবো ।

এখন আমাদের কে ইউজার আইডি নিতে হবে অর্থাৎ ইউজার যখন কোন কিছু পোস্ট করবে তখন stream এর সাথে সাথে ঐ ইউজারের আইডি ও নিয়ে যেয়ে ডাটাবেজে সেভ রাখবো এবং সেটাকে redux store এও রাখবো । এখন action/index.js ফাইলে যেয়ে নিচের কোড গুলো লিখি ।

এখানে async dispatch থেকে শেষের রিটার্ন পর্যন্ত কাজ গুলো redux-thunk অটোম্যাটিক কল করে থাকে । dispatch এর সাথে সাথে getState ও আর্গুমেন্ট হিশেবে পাস করা যায় । এটার কাজ হচ্ছে redux store থেকে প্রয়োজন মতো ডাটা নিয়ে আসতে পারে । এখানে আমরা getState থেকে userId ডিস্ত্রাকচার করে নিয়েছি । এরপর সেটাকে formValues এর সাথে পাঠিয়ে দিয়েছি ।

এখন কোন ইউজার stream তৈরি করার সময় ডাটার সাথে সাথে userId ও পাঠিয়ে দিয়েছি ।

Conditionally showing edit and delete button

এখানে আমরা প্রথমে mapStateToProps থেকে auth থেকে userId এক্সট্রাক্ট করে নিয়েছি , এরপর renderAdmin() নামে একটি আলাদা মেথডের ভেতর আমরা চেক করেছি , যে লিস্ট stream তৈরি করেছে তার ইউজার আইডি এইটাই নাকি , যদি user authenticate হয় তাহলে edit, delete বাটন দেখাব ।

এই মেথডটি আমরা কল করবো renderList() মেথডের ভেতর থেকে , <i /> ট্যাগের ঠিক উপরেই ।

{ this.renderAdmin(stream) }<i className="large middle aligned icon camera" />

এখন আমরা streams/new রুটে যেয়ে কিছু নতুন stream তৈরি করে আবার / রুট রাউটারে চলে আসি, এখন দেখবো যে সাইন ইন করে যে stream গুলো তৈরি করেছিলাম সেগুলোর পাশে edit, delete বাটন আছে আর অন্য গুলোতে নাই ।

রিডাক্স স্টোরে গেলে দেখা যাবে যে ইউজার সাইন ইন থাকলে user id টাও সেভ করে রাখে । এখন সাইন আউট করলে বাটন গুলো ভেনিস হয়ে যাবে সাথে সাথে ।

Linking to stream creation:

এখন ইউজার লগইন অবস্থায় থাকলে আমরা stream list এর নিচে Create Stream নামে একটি বাটন রাখবো কন্ডিশনালি ।

user login অবস্থায় থাকলে auth এর ভেতর আমরা isSignedIn নামে একটি প্রপার্টি আছে সেটার ভেলু boolean . এটাকে প্রথমে স্টোর থেকে extract করে নিয়ে আসবো mapStateToProps() মেথডের ভেতর ।

isSignedIn: state.auth.isSignedIn

এরপর renderList নামে একটি মেথড তৈরি করে render() এর ভেতর এটাকে কল করে দিবো । navigation এর জন্য আমরা Link কম্পোনেন্ট ব্যাবহার করবো সেজন্য এটাকে প্রথমে import করে নিবো ।

import { Link } from 'react-router-dom'

এটা এখন create পেজে চলে যাবে ।

Programmatic navigation :

intentional navigation এ আমরা Link ট্যাগের ভেতর রাউট দিয়ে দেই আর Link ট্যাগ রাখি BrowserRouter এর ভেতর ।

কিন্তু এখন আমরা এটাকে রাখবো Router এর ভেতর । তাহলে যেকোনো জায়গা থেকে এটাকে ব্যাবহার করতে পারবো ।

stream create এ আমরা যখন কোনও ফর্ম সাবমিট করবো তখন সাকসেস ফুল ভাবে সাবমিট হলে রুট রাউটারে রিডিরেক্ট করে দিবো । আর না হলে stream create এর এখানেই থাকবে ।

সাকসেসফুল ভাবে সাবমিট হয়েছে কিনা সেটা আসলে আমরা দেখতে পারবো action creator এ , কারণ এই একশন ক্রিয়েটর এই ফর্মের ভেলু সাবমিট করার কোড লিখা আছে সেখান থেকেই ডাটাকে redux store এ dispatch করা হয় , dispatch হয়ে আসলে এরপর আমরা রুট ডিরেক্টরিতে পুশ করবো ।

এখন প্রথমে root directory তে history.js নামে একটি ফাইল তৈরি করে নিচের কোড গুলো লিখি ।

import { createBrowserHistory } from 'history'export default createBrowserHistory();

এখন BrowserRouter কে নরমাল Router এ কনভার্ট করবো । আগে history কে import করে নিবো ।

import history from '../history';<Router history={ history }>

আগে এখানে Router এর জায়গায় ছিল BrowserRouter.

এরপর actions/index.js ফাইলে গিয়ে এই history ফাইলকে প্রথমে import করে নিয়ে এরপর route কে push করে দিবো ।

এভাবে dispatch এরপর push() করে দিবো ।

manually changing api record

db.json ফাইল ওপেন করে ম্যানুয়ালি কোনও ডাটা ডিলিট করে দিতে পারি । জেসন ফাইল কোন কিছু পরিবর্তন হলে সাথে সাথে server re run হয় ।

URL based selection

url based selection এ ঐ আইডি url এ যেয়ে এরপর ডাটা ব্যাবহার করবে আর selection Reducer এ কোথাও ক্লিক করলে সেখান থেকে ডাটা নিয়ে কাজ করে ।

এজন্য প্রথমে streamList কম্পোনেন্টে renderAdmin() মেথডে button এর পরিবর্তে Link ট্যাগ ব্যাবহার করবো ।

<Link to={ `/streams/edit/${stream.id}` } className="ui button primary">Edit</Link>

আর App.js ফাইলে নিচের মতো রাউট তৈরি করবো । clone দিয়ে এরপর id দিবো । এই id হচ্ছে একধরনের ভেরিয়েবল ।

<Route path="/streams/edit/:id" exact component={StreamEdit} />

more on route params

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

const StreamEdit = (props) => {
console.log(props)
return <div>StreamEdit</div>
}

এখানে ৩ টা অবজেক্ট পাবো history, location, match . এরমধ্যে match অবজেক্টে গেলে দেখা যাবে যে যে প্যারামিটার পাঠিয়েছিলাম সেগুলো এখানে params এর মধ্যে আছে ।

Selecting record from state :

mapStateToProps এর সাথে সব সময় ২ টা প্রপ্স পাস হয় । একটা হচ্ছে পুরো redux store এর অবজেক্ট state আরেকটা হচ্ছে এই কম্পোনেন্টে প্রপ্স কি আছে সেটা ownProps । ownProps কনসল লগ করলে আউটপুটে এই কম্পোনেন্টের প্রপ্স ই দেখা যাবে ।

এখানে আমরা এই ownProps এক্সেস করে সেখান থেকে id নিয়ে ঐ আইডি ধরে স্টোর থেকে ঐ আইডির ডাটা এক্সেস করেছি ।

console.log(props.stream)

এখন props.stream কে কনসল লগ করলে ঐ আইডি রিলেটেড ডাটা দেখা যাবে । কিন্তু এখানে সমস্যা হচ্ছে যে আমরা সরাসরি যদি এন্ড পয়েন্টে হিট করি তাহলে undefined আসবে কারণ রিফ্রেশ করলে রিডাক্স স্টোর ফাকা হয়ে যায় কারণ কোনও action creator এ কল হয় না , আর action creator এ কল না হলে রিডাক্স স্টোরে ডাটা আপডেট হবে না । তখন আবার রুট url এ যেয়ে ডাটা লোড করার পর এই লিঙ্কে আসলে তখন ডাটা দেখা যাবে অর্থাৎ এই লিঙ্ক অন্য আরেকটি লিঙ্কের উপর ডিপেন্ডেন্ট , এই বাগ ফিক্স করার জন্য আমাদের কিছু কাজ করতে হবে ।

এজন্য react-router এ ডাটা isolation এর দরকার হয়। অর্থাৎ আমরা প্রথমে fetchStream নামে action creator কে কল করবো ডাটা লোড করার জন্য এরপর mapStateToProps থেকে ঐ আইডির ডাটা নিয়ে আসবো ।

এখন আমাদের ডাটা ঠিক মতো লোড হবে ।

Code reuse

এখানে stream create এবং stream edit পেজ মোটামুটি একি । এখন আমরা একটি চাইল্ড কম্পোনেন্ট তৈরি করবো যেটার স্ত্রাকচার দুইটার জন্য ই কমন , শুধু ডাটা আলাদা হবে ।

এখন প্রথমে StreamCreate কম্পোনেন্টের সকল কোড কপি করে নিয়ে নতুন একটি ফাইলে রাখবো , এই ফাইলে নাম দিবো StreamForm.js. এটা জেনারেলাইজ ফর্ম । নিচের কোড গুলো লিখি ।

এই ফর্ম কে আমরা রি ইউজ করতে চাচ্ছি ।

এজন্য আমরা পাশের এই স্ট্রাকচার ফলো করবো ।

StreamForm নামে একটি কম্পোনেন্ট তৈরি করবো , এখানে শুধু ফর্মের ইনফরমেশন ই থাকবে ।

এটাকে আমরা StreamCreate এবং StreamEdit এর চাইল্ড কম্পোনেন্ট হিশেবে ব্যাবহার করবো ।

StreamCreate কম্পোনেন্ট কে রি ফ্যাক্টর করে উপরের মতো করে তৈরি করবো । এখানে onSubmit কে কলব্যাক ফাংশন হিশেবে props আকারে চাইল্ডে পাঠিয়েছি । ইউজার ফর্মে সাবমিট করলে সেই ডাটা নিয়ে এসে StreamCreate এর onSubmit এ কল করবে এখানে থেকে আমরা createStream() নামে action creator এ কল করেছি । এই action creator এই ডাটা কে নিয়ে ডাটা বেজে পোস্ট রিকোয়েস্ট করে ডাটা সেভ করবে ।

StreamEdit কম্পোনেন্ট ও একি ভাবে কাজ করবে শুধু হাল্কা একটা পার্থক্য হচ্ছে ওখানে শুধু initial ভেলু টাকে ধরিয়ে দিতে হবে ।

এখন StreamEdit কম্পোনেন্টে নিচের কোড গুলো লিখি ।

প্রথমে url এ আমরা যে আইডি পাঠিয়েছি সেটা ধরে আমরা componentDidMount() এ ঐ আইডির আন্ডারে যে ডাটা গুলো আছে সেগুলো fetchStream একশন ক্রিয়েটর কল করে নিয়ে আসবো ।

এরপর <StreamForm> হচ্ছে একটি চাইল্ড কম্পোনেন্ট , এখানে ইনিশিয়াল ভেলু পাঠানোর জন্য redux form এর বিল্ড ইন একটি props আছে সেটা হচ্ছে initialValues । এখানে এটা একটি অবজেক্ট নেয় ।

অবজেক্টের ভেতর ইনিশিয়াল ভেলু পাস করে দিতে পারি । এখানে আমরা lodash লাইব্রেরির pick() মেথড ব্যাবহার করেছি । pick() মেথডের প্রথম প্যারামিটার হচ্ছে কোন অবজেক্ট থেকে ডাটা এক্সট্রাক্ট করবো আর এর পরের আর্গুমেন্ট গুলো হচ্ছে কি কি ডাটা আমরা এক্ট্রাক্ট করতে চাচ্ছি ।

এছাড়াও প্রথমে আমরা props.streams পাঠালে সকল ডাটাই পাঠিয়ে দিবে,যখন আমরা ডাটাবেজে আপডেট করতে যাবো তখন id, userid সহ আপডেট হয়ে যাবে কিন্তু এটা করলে ওয়ার্নিং দিবে কারণ আমরা জাস্ট title এবং description আপডেট করতে চাচ্ছি ।

কারণ আমরা যখন ঐ আইডির সকল ডাটা পাঠাই তখন redux form এর value তে সকল ডাটা নিয়ে নেয় আবার যখন বেক করে তখন id, user id সহ সকল ডাটা বেক করে ।

initialValues={{
title: this.props.stream.title,
description: this.props.stream.description
}}

এভাবে ডাটা পাঠালে redux form এ title এবং description আছে সেটার সাথে ম্যাপ করে ডাটা ইন্সারট করে ।

এখন একশন ক্রিয়েটরে কিছুটা পরিবর্তন করতে হবে ।

export const editStream = (id, formValues) => async dispatch => {
const response = await streams.patch(`/streams/${id}`, formValues);
dispatch({ type: EDIT_STREAM, payload: response.data })
history.push('/')
}

এখানে আগে put ছিল এখন আমরা patch ব্যাবহার করেছি ।

এর কারণ হচ্ছে put ব্যাবহার করলে সকল ডাটা আপডেট করে নেয় কিন্তু patch ব্যাবহার করলে শুধু যে ডাটাগুলো আপডেট করতে চাই সেই ডাটাগুলোই আপডেট হবে ।

আর আপডেট হওয়ার পর history.push(‘/’) দিয়ে হোম পেজে redirect করে দিয়েছি ।

--

--