ไม่เปลี่ยนรูปใน JavaScript โดยใช้ Redux

เผยแพร่แล้ว: 2022-03-11

ในระบบนิเวศน์ที่เพิ่มขึ้นเรื่อยๆ ของแอปพลิเคชัน JavaScript ที่สมบูรณ์และซับซ้อน มีสถานะที่ต้องจัดการมากกว่าที่เคย: ผู้ใช้ปัจจุบัน รายการโพสต์ที่โหลด ฯลฯ

ชุดข้อมูลใดๆ ที่ต้องมีประวัติเหตุการณ์สามารถถือเป็นการเก็บบันทึกสถานะได้ สถานะการจัดการอาจทำได้ยากและเกิดข้อผิดพลาดได้ง่าย แต่การทำงานกับข้อมูลที่ไม่เปลี่ยนรูป (แทนที่จะเปลี่ยนแปลงได้) และเทคโนโลยีสนับสนุนบางอย่าง เช่น Redux ตามวัตถุประสงค์ของบทความนี้ สามารถช่วยได้มาก

ข้อมูลที่ไม่เปลี่ยนรูปมีข้อจำกัด กล่าวคือ เมื่อสร้างขึ้นแล้วจะไม่สามารถเปลี่ยนแปลงได้ แต่ยังมีประโยชน์มากมาย โดยเฉพาะในการอ้างอิงเทียบกับความเท่าเทียมกันของมูลค่า ซึ่งจะช่วยเร่งความเร็วของแอปพลิเคชันที่ต้องอาศัยการเปรียบเทียบข้อมูลบ่อยๆ (ตรวจสอบว่ามีสิ่งใดจำเป็นต้องอัปเดตหรือไม่ , ตัวอย่างเช่น).

การใช้สถานะที่ไม่เปลี่ยนรูปทำให้เราสามารถเขียนโค้ดที่สามารถบอกได้อย่างรวดเร็วว่าสถานะมีการเปลี่ยนแปลงหรือไม่ โดยไม่จำเป็นต้องทำการเปรียบเทียบข้อมูลแบบเรียกซ้ำ ซึ่งมักจะเร็วกว่ามาก

บทความนี้จะครอบคลุมการใช้งานจริงของ Redux เมื่อจัดการสถานะผ่านผู้สร้างการกระทำ ฟังก์ชันล้วนๆ ตัวลดที่ประกอบด้วย การกระทำที่ไม่บริสุทธิ์ด้วย Redux-saga และ Redux Thunk และสุดท้ายคือการใช้ Redux กับ React ที่กล่าวว่ามีทางเลือกมากมายสำหรับ Redux เช่นไลบรารีที่ใช้ MobX, Relay และ Flux

ทำไมต้อง Redux?

ลักษณะสำคัญที่แยก Redux ออกจากคอนเทนเนอร์สถานะอื่น ๆ ส่วนใหญ่ เช่น MobX, Relay และการใช้งาน Flux อื่น ๆ ส่วนใหญ่คือ Redux มีสถานะเดียวที่สามารถแก้ไขได้ผ่าน "การกระทำ" เท่านั้น (วัตถุ JavaScript ธรรมดา) ซึ่งถูกส่งไปยัง ร้านรีดักซ์. ที่เก็บข้อมูลอื่น ๆ ส่วนใหญ่มีสถานะอยู่ในส่วนประกอบ React เอง อนุญาตให้คุณมีหลายร้านค้าและ/หรือใช้สถานะที่ไม่แน่นอน

ซึ่งจะทำให้ตัวรีดิวเซอร์ของร้านค้า ซึ่งเป็นฟังก์ชันแท้ที่ทำงานบนข้อมูลที่ไม่เปลี่ยนรูป ดำเนินการและอาจอัปเดตสถานะ กระบวนการนี้บังคับใช้การไหลของข้อมูลแบบทิศทางเดียว ซึ่งง่ายต่อการเข้าใจและกำหนดขึ้นได้

กระแส Redux

เนื่องจากรีดิวเซอร์ Redux เป็นฟังก์ชันล้วนๆ ที่ทำงานบนข้อมูลที่ไม่เปลี่ยนรูป พวกมันจึงสร้างเอาต์พุตเดียวกันโดยให้อินพุตเดียวกันเสมอ ทำให้ง่ายต่อการทดสอบ นี่คือตัวอย่างของตัวลด:

 import Immutable from 'seamless-immutable' const initialState = Immutable([]) // create immutable array via seamless-immutable /** * a reducer takes a state (the current state) and an action object (a plain JavaScript object that was dispatched via dispatch(..) and potentially returns a new state. */ function addUserReducer(state = initialState, action) { if (action.type === 'USERS_ADD') { return state.concat(action.payload) } return state // note that a reducer MUST return a value } // somewhere else... store.dispatch({ type: 'USERS_ADD', payload: user }) // dispatch an action that causes the reducer to execute and add the user

การจัดการกับฟังก์ชันบริสุทธิ์ช่วยให้ Redux รองรับกรณีการใช้งานจำนวนมากซึ่งโดยทั่วไปมักไม่สามารถทำได้ง่ายด้วยสถานะกลายพันธุ์ เช่น:

  • การเดินทางข้ามเวลา (ย้อนเวลากลับไปสู่สถานะก่อนหน้า)
  • การบันทึก (ติดตามทุกการกระทำเพื่อค้นหาสาเหตุที่ทำให้เกิดการกลายพันธุ์ในร้านค้า)
  • สภาพแวดล้อมการทำงานร่วมกัน (เช่น GoogleDocs ซึ่งการกระทำเป็นออบเจ็กต์ JavaScript ธรรมดาและสามารถทำให้เป็นอนุกรม ส่งผ่านสาย และเล่นซ้ำบนเครื่องอื่นได้)
  • การรายงานจุดบกพร่องอย่างง่าย (เพียงส่งรายการการดำเนินการที่ส่งไป และเล่นซ้ำเพื่อให้ได้สถานะเดียวกัน)
  • การเรนเดอร์ที่ปรับให้เหมาะสมที่สุด (อย่างน้อยในเฟรมเวิร์กที่แสดง DOM เสมือนเป็นหน้าที่ของสถานะ เช่น React: เนื่องจากไม่สามารถเปลี่ยนแปลงได้ คุณสามารถบอกได้อย่างง่ายดายว่ามีบางอย่างเปลี่ยนแปลงไปโดยการเปรียบเทียบข้อมูลอ้างอิง เมื่อเทียบกับการเปรียบเทียบวัตถุแบบเรียกซ้ำ)
  • ทดสอบรีดิวเซอร์ของคุณอย่างง่ายดาย เนื่องจากฟังก์ชันบริสุทธิ์สามารถทดสอบหน่วยได้อย่างง่ายดาย

ผู้สร้างแอคชั่น

ผู้สร้างการกระทำของ Redux ช่วยในการรักษาโค้ดให้สะอาดและทดสอบได้ โปรดจำไว้ว่า “การกระทำ” ใน Redux นั้นไม่มีอะไรมากไปกว่าวัตถุ JavaScript ธรรมดาที่อธิบายการกลายพันธุ์ที่ควรจะเกิดขึ้น ดังที่กล่าวไปแล้ว การเขียนวัตถุเดิมซ้ำแล้วซ้ำอีกนั้นซ้ำแล้วซ้ำเล่าและเกิดข้อผิดพลาดได้ง่าย

ผู้สร้างการกระทำใน Redux เป็นเพียงฟังก์ชันตัวช่วยที่ส่งคืนวัตถุ JavaScript ธรรมดาที่อธิบายการกลายพันธุ์ ซึ่งช่วยลดโค้ดที่ซ้ำซ้อน และเก็บการกระทำทั้งหมดของคุณไว้ในที่เดียว:

 export function usersFetched(users) { return { type: 'USERS_FETCHED', payload: users, } } export function usersFetchFailed(err) { return { type: 'USERS_FETCH_FAILED', payload: err, } } // reducer somewhere else... const initialState = Immutable([]) // create immutable array via seamless-immutable /** * a reducer takes a state (the current state) and an action object (a plain JavaScript object that was dispatched via dispatch(..) and potentially returns a new state. */ function usersFetchedReducer(state = initialState, action) { if (action.type === 'USERS_FETCHED') { return Immutable(action.payload) } return state // note that a reducer MUST return a value }

การใช้ Redux กับ Immutable Libraries

ในขณะที่ธรรมชาติของตัวลดขนาดและการดำเนินการทำให้ง่ายต่อการทดสอบ หากไม่มีไลบรารีตัวช่วยที่ไม่สามารถเปลี่ยนแปลงได้ ก็ไม่มีอะไรป้องกันคุณจากการกลายพันธุ์ของวัตถุ ซึ่งหมายความว่าการทดสอบสำหรับตัวลดขนาดทั้งหมดของคุณจะต้องแข็งแกร่งเป็นพิเศษ

พิจารณาตัวอย่างโค้ดต่อไปนี้ของปัญหาที่คุณจะเจอโดยไม่มีไลบรารี่เพื่อปกป้องคุณ:

 const initialState = [] function addUserReducer(state = initialState, action) { if (action.type === 'USERS_ADD') { state.push(action.payload) // NOTE: mutating action!! return state } return state // note that a reducer MUST return a value }

ในตัวอย่างโค้ดนี้ การเดินทางข้ามเวลาจะใช้งานไม่ได้เนื่องจากสถานะก่อนหน้าจะเหมือนกับสถานะปัจจุบัน ส่วนประกอบบริสุทธิ์อาจไม่อัปเดต (หรือแสดงผลซ้ำ) เนื่องจากการอ้างอิงไปยังสถานะไม่มีการเปลี่ยนแปลงแม้ว่าข้อมูลดังกล่าว มีมีการเปลี่ยนแปลงและการกลายพันธุ์นั้นยากกว่าที่จะให้เหตุผล

หากไม่มีไลบรารีที่เปลี่ยนแปลงไม่ได้ เราจะสูญเสียผลประโยชน์ทั้งหมดที่ Redux มอบให้ ดังนั้นจึงขอแนะนำอย่างยิ่งให้ใช้ไลบรารีตัวช่วยที่ไม่สามารถเปลี่ยนแปลงได้ เช่น immutable.js หรือ seamless-immutable โดยเฉพาะอย่างยิ่งเมื่อทำงานในทีมขนาดใหญ่ที่มีหลายมือสัมผัสโค้ด

ไม่ว่าคุณจะใช้ไลบรารี่ใด Redux จะทำงานเหมือนกัน มาเปรียบเทียบข้อดีข้อเสียของทั้งสองอย่างกัน เพื่อที่คุณจะได้เลือกข้อใดที่เหมาะสมที่สุดสำหรับกรณีการใช้งานของคุณ:

ไม่เปลี่ยนรูป.js

Immutable.js เป็นห้องสมุดที่สร้างโดย Facebook โดยมีลักษณะการทำงานมากกว่าในโครงสร้างข้อมูล เช่น แผนที่ รายการ ชุด และลำดับ ไลบรารีของโครงสร้างข้อมูลถาวรที่ไม่เปลี่ยนรูปแบบดำเนินการคัดลอกน้อยที่สุดเท่าที่จะเป็นไปได้ระหว่างสถานะต่างๆ

ข้อดี:

  • การแบ่งปันโครงสร้าง
  • มีประสิทธิภาพมากขึ้นในการอัพเดท
  • หน่วยความจำมีประสิทธิภาพมากขึ้น
  • มีชุดวิธีการช่วยเหลือในการจัดการการอัปเดต

จุดด้อย:

  • ไม่ทำงานอย่างราบรื่นกับไลบรารี JS ที่มีอยู่ (เช่น lodash, ramda)
  • ต้องการการแปลงเป็นและกลับจาก (toJS / fromJS) โดยเฉพาะอย่างยิ่งในระหว่างการให้น้ำ / การคายน้ำและการเรนเดอร์

ไม่มีรอยต่อ-ไม่เปลี่ยนรูป

Seamless-immutable คือไลบรารีสำหรับข้อมูลที่ไม่เปลี่ยนรูปซึ่งเข้ากันได้แบบย้อนหลังไปจนถึง ES5

มันขึ้นอยู่กับฟังก์ชันการกำหนดคุณสมบัติ ES5 เช่น defineProperty(..) เพื่อปิดใช้งานการกลายพันธุ์บนวัตถุ ดังนั้น มันจึงเข้ากันได้อย่างสมบูรณ์กับไลบรารีที่มีอยู่เช่น lodash และ Ramda นอกจากนี้ยังสามารถปิดการใช้งานในรุ่นที่ใช้งานจริงได้ ซึ่งอาจให้ประสิทธิภาพที่เพิ่มขึ้นอย่างมีนัยสำคัญ

ข้อดี:

  • ทำงานได้อย่างราบรื่นกับไลบรารี JS ที่มีอยู่ (เช่น lodash, ramda)
  • ไม่ต้องใช้โค้ดเพิ่มเติมเพื่อรองรับการแปลง
  • การตรวจสอบสามารถปิดใช้งานได้ในบิลด์ที่ใช้งานจริง เพิ่มประสิทธิภาพ

จุดด้อย:

  • ไม่มีการแชร์โครงสร้าง - อ็อบเจ็กต์ / อาร์เรย์ถูกคัดลอกแบบตื้น ทำให้ชุดข้อมูลขนาดใหญ่ช้าลง
  • หน่วยความจำไม่มีประสิทธิภาพ

Redux และตัวลดหลายตัว

คุณสมบัติที่มีประโยชน์อีกอย่างของ Redux คือความสามารถในการเขียนตัวลดร่วมกัน สิ่งนี้ช่วยให้คุณสร้างแอปพลิเคชันที่ซับซ้อนมากขึ้น และในแอปพลิเคชันที่มีขนาดที่ประเมินค่าได้ คุณจะมีสถานะหลายประเภทอย่างหลีกเลี่ยงไม่ได้ (ผู้ใช้ปัจจุบัน รายการโพสต์ที่โหลด เป็นต้น) Redux สนับสนุน (และสนับสนุน) กรณีการใช้งานนี้โดยให้ฟังก์ชันที่ combineReducers เป็นธรรมชาติ:

 import { combineReducers } from 'redux' import currentUserReducer from './currentUserReducer' import postsListReducer from './postsListReducer' export default combineReducers({ currentUser: currentUserReducer, postsList: postsListReducer, })

ด้วยโค้ดข้างต้น คุณสามารถมีส่วนประกอบที่อาศัย currentUser และส่วนประกอบอื่นที่อาศัย postsList สิ่งนี้ยังช่วยปรับปรุงประสิทธิภาพ เนื่องจากองค์ประกอบเดียวใดๆ จะสมัครเฉพาะสาขาใดๆ ของทรีที่เกี่ยวข้องเท่านั้น

การกระทำที่ไม่บริสุทธิ์ใน Redux

โดยค่าเริ่มต้น คุณสามารถส่งออบเจ็กต์ JavaScript ธรรมดาไปยัง Redux เท่านั้น อย่างไรก็ตาม ด้วยมิดเดิลแวร์ Redux สามารถรองรับการกระทำที่ไม่บริสุทธิ์ เช่น การรับเวลาปัจจุบัน ดำเนินการตามคำขอของเครือข่าย การเขียนไฟล์ไปยังดิสก์ และอื่นๆ

'มิดเดิลแวร์' เป็นคำที่ใช้สำหรับฟังก์ชันที่สามารถสกัดกั้นการกระทำที่ถูกส่งออกไป เมื่อสกัดกั้นแล้ว ก็สามารถทำสิ่งต่างๆ เช่น แปลงการกระทำหรือส่งการกระทำแบบอะซิงโครนัสได้ เช่นเดียวกับมิดเดิลแวร์ในเฟรมเวิร์กอื่นๆ (เช่น Express.js)

ไลบรารีมิดเดิลแวร์ทั่วไปสองไลบรารีคือ Redux Thunk และ Redux-saga Redux Thunk เขียนในลักษณะจำเป็น ขณะที่ Redux-saga เขียนในลักษณะการใช้งาน ลองเปรียบเทียบทั้งสอง

Redux Thunk

Redux Thunk รองรับการกระทำที่ไม่บริสุทธิ์ภายใน Redux โดยใช้ thunks ซึ่งเป็นฟังก์ชันที่ส่งคืนฟังก์ชันอื่นๆ ที่เชื่อมโยงได้ ในการใช้ Redux-Thunk คุณต้องติดตั้งมิดเดิลแวร์ Redux Thunk กับสโตร์ก่อน:

 import { createStore, applyMiddleware } from 'redux' import thunk from 'redux-thunk' const store = createStore( myRootReducer, applyMiddleware(thunk), // here, we apply the thunk middleware to R )

ตอนนี้ เราสามารถดำเนินการที่ไม่บริสุทธิ์ (เช่น การเรียก API) โดยส่ง thunk ไปที่ Redux store:

 store.dispatch( dispatch => { return api.fetchUsers() .then(users => dispatch(usersFetched(users)) // usersFetched is a function that returns a plain JavaScript object (Action) .catch(err => dispatch(usersFetchError(err)) // same with usersFetchError } )

สิ่งสำคัญที่ควรทราบคือการใช้ thunks อาจทำให้โค้ดของคุณทดสอบได้ยาก และทำให้การให้เหตุผลผ่านโฟลว์โค้ดยากขึ้น

Redux-saga

Redux-saga รองรับการกระทำที่ไม่บริสุทธิ์ผ่านคุณสมบัติ ES6 (ES2015) ที่เรียกว่าเครื่องกำเนิดและไลบรารีของผู้ช่วยที่ทำงาน / บริสุทธิ์ สิ่งที่ยอดเยี่ยมเกี่ยวกับตัวสร้างคือสามารถกลับมาทำงานต่อและหยุดชั่วคราวได้ และสัญญา API ของพวกเขาทำให้ง่ายต่อการทดสอบ

มาดูกันว่าเราจะสามารถปรับปรุงความสามารถในการอ่านและทดสอบของวิธีการ thunk ก่อนหน้าโดยใช้ sagas ได้อย่างไร!

ขั้นแรก ให้ติดตั้งมิดเดิลแวร์ Redux-saga กับร้านค้าของเรา:

 import { createStore, applyMiddleware } from 'redux' import createSagaMiddleware from 'redux-saga' import rootReducer from './rootReducer' import rootSaga from './rootSaga' // create the saga middleware const sagaMiddleware = createSagaMiddleware() // mount the middleware to the store const store = createStore( rootReducer, applyMiddleware(sagaMiddleware), ) // run our saga! sagaMiddleware.run(rootSaga)

โปรดทราบว่าฟังก์ชัน run(..) จะต้องถูกเรียกใช้ด้วยเทพนิยายจึงจะเริ่มดำเนินการได้

ตอนนี้มาสร้างเทพนิยายของเรา:

 import { call, put, takeEvery } from 'redux-saga/effects' // these are saga effects we'll use export function *fetchUsers(action) { try { const users = yield call(api.fetchUsers) yield put(usersFetched(users)) } catch (err) { yield put(usersFetchFailed(err)) } } export default function *rootSaga() { yield takeEvery('USERS_FETCH', fetchUsers) }

เรากำหนดฟังก์ชันตัวสร้างสองแบบ ฟังก์ชันหนึ่งดึงรายชื่อผู้ใช้และ rootSaga ขอให้สังเกตว่าเราไม่ได้เรียก api.fetchUsers โดยตรง แต่ให้ผลในวัตถุการโทรแทน นี่เป็นเพราะ Redux-saga สกัดกั้นวัตถุการโทรและเรียกใช้ฟังก์ชันที่มีอยู่ภายในเพื่อสร้างสภาพแวดล้อมที่บริสุทธิ์ (เท่าที่เครื่องกำเนิดของคุณเกี่ยวข้อง)

rootSaga ให้ผลการเรียกครั้งเดียวไปยังฟังก์ชันที่เรียกว่า takeEvery, ซึ่งดำเนินการทุกอย่างที่ส่งด้วยประเภท USERS_FETCH และเรียก fetchUsers saga ด้วยการดำเนินการที่ใช้ อย่างที่เราเห็น วิธีนี้สร้างแบบจำลองผลข้างเคียงที่คาดเดาได้สำหรับ Redux ซึ่งทำให้ง่ายต่อการทดสอบ!

การทดสอบซากัส

มาดูกันว่าเครื่องกำเนิดไฟฟ้าทำให้นิยายของเราทดสอบง่ายได้อย่างไร เราจะใช้มอคค่าในส่วนนี้เพื่อเรียกใช้การทดสอบหน่วยและชัยเพื่อยืนยัน

เนื่องจาก sagas สร้างอ็อบเจกต์ JavaScript ธรรมดาๆ และรันภายในตัวสร้าง เราสามารถทดสอบได้อย่างง่ายดายว่าพวกมันทำงานอย่างถูกต้องโดยไม่ต้องเยาะเย้ยเลย! โปรดทราบว่า call , take , put ฯลฯ เป็นเพียงออบเจ็กต์ JavaScript ธรรมดาที่มิดเดิลแวร์ Redux-saga สกัดกั้น

 import { take, call } from 'redux-saga/effects' import { expect } from 'chai' import { rootSaga, fetchUsers } from '../rootSaga' describe('saga unit test', () => { it('should take every USERS_FETCH action', () => { const gen = rootSaga() // create our generator iterable expect(gen.next().value).to.be.eql(take('USERS_FETCH')) // assert the yield block does have the expected value expect(gen.next().done).to.be.equal(false) // assert that the generator loops infinitely }) it('should fetch the users if successful', () => { const gen = fetchUsers() expect(gen.next().value).to.be.eql(call(api.fetchUsers)) // expect that the call effect was yielded const users = [ user1, user2 ] // some mock response expect(gen.next(users).value).to.be.eql(put(usersFetched(users)) }) it('should fail if API fails', () => { const gen = fetchUsers() expect(gen.next().value).to.be.eql(call(api.fetchUsers)) // expect that the call effect was yielded const err = { message: 'authentication failed' } // some mock error expect(gen.throw(err).value).to.be.eql(put(usersFetchFailed(err)) }) })

การทำงานกับ React

แม้ว่า Redux จะไม่ผูกติดอยู่กับไลบรารี่ที่แสดงร่วมใด ๆ แต่ก็ทำงานได้ดีกับ React.js เนื่องจากส่วนประกอบ React เป็นฟังก์ชันบริสุทธิ์ที่รับสถานะเป็นอินพุตและสร้าง DOM เสมือนเป็นเอาต์พุต

React-Redux เป็นไลบรารีตัวช่วยสำหรับ React และ Redux ที่ขจัดการทำงานหนักส่วนใหญ่ในการเชื่อมต่อทั้งสอง เพื่อให้ใช้ React-Redux ได้อย่างมีประสิทธิภาพมากที่สุด มาดูแนวคิดของส่วนประกอบการนำเสนอและส่วนประกอบคอนเทนเนอร์กัน

องค์ประกอบการนำเสนอจะอธิบายว่าสิ่งต่าง ๆ ควรมีลักษณะอย่างไร ขึ้นอยู่กับอุปกรณ์ประกอบฉากที่จะแสดงผลเท่านั้น พวกเขาเรียกใช้การเรียกกลับจากอุปกรณ์ประกอบฉากเพื่อดำเนินการจัดส่ง พวกเขาเขียนด้วยมือล้วนๆ และไม่ผูกติดอยู่กับระบบการจัดการของรัฐอย่าง Redux

ในทางกลับกัน องค์ประกอบของคอนเทนเนอร์อธิบายว่าสิ่งต่าง ๆ ควรทำงานอย่างไร รับรู้ Redux ส่งการกระทำของ Redux โดยตรงเพื่อทำการกลายพันธุ์ และโดยทั่วไปจะถูกสร้างขึ้นโดย React-Redux พวกเขามักจะจับคู่กับองค์ประกอบการนำเสนอโดยจัดให้มีอุปกรณ์ประกอบฉาก

ส่วนประกอบการนำเสนอและส่วนประกอบคอนเทนเนอร์ใน Redux

มาเขียนองค์ประกอบการนำเสนอและเชื่อมต่อกับ Redux ผ่าน React-Redux:

 const HelloWorld = ({ count, onButtonClicked }) => ( <div> <span>Hello! You've clicked the button {count} times!</span> <button onClick={onButtonClicked}>Click me</button> </div> ) HelloWorld.propTypes = { count: PropTypes.number.isRequired, onButtonClicked: PropTypes.func.isRequired, }

โปรดทราบว่านี่เป็นองค์ประกอบที่ "โง่" ที่ต้องอาศัยอุปกรณ์ประกอบฉากในการทำงาน วิธีนี้ยอดเยี่ยมเพราะทำให้ส่วนประกอบ React ง่ายต่อการทดสอบและเขียนได้ง่าย มาดูวิธีเชื่อมต่อส่วนประกอบนี้กับ Redux กันก่อน แต่ก่อนอื่นเรามาพูดถึงว่าส่วนประกอบลำดับที่สูงกว่าคืออะไร

ส่วนประกอบการสั่งซื้อที่สูงขึ้น

React-Redux มีฟังก์ชันตัวช่วยที่เรียกว่า connect( .. ) ซึ่งสร้างส่วนประกอบลำดับที่สูงกว่าจากส่วนประกอบ React "ใบ้" ที่รับรู้ Redux

React เน้นย้ำถึงความสามารถในการขยายและนำกลับมาใช้ใหม่ได้ผ่านการจัดองค์ประกอบ ซึ่งเป็นเวลาที่คุณห่อส่วนประกอบในส่วนประกอบอื่นๆ การห่อส่วนประกอบเหล่านี้สามารถเปลี่ยนลักษณะการทำงานหรือเพิ่มฟังก์ชันการทำงานใหม่ได้ มาดูกันว่าเราจะสร้างองค์ประกอบการสั่งซื้อที่สูงขึ้นจากองค์ประกอบการนำเสนอของเราที่รับรู้ Redux ได้อย่างไร - ส่วนประกอบคอนเทนเนอร์

นี่คือวิธีการ:

 import { connect } from 'react-redux' const mapStateToProps = state => { // state is the state of our store // return the props that we want to use for our component return { count: state.count, } } const mapDispatchToProps = dispatch => { // dispatch is our store dispatch function // return the props that we want to use for our component return { onButtonClicked: () => { dispatch({ type: 'BUTTON_CLICKED' }) }, } } // create our enhancer function const enhancer = connect(mapStateToProps, mapDispatchToProps) // wrap our "dumb" component with the enhancer const HelloWorldContainer = enhancer(HelloWorld) // and finally we export it export default HelloWorldContainer

โปรดทราบว่าเราได้กำหนดสองฟังก์ชัน mapStateToProps และ mapDispatchToProps

mapStateToProps เป็นฟังก์ชันแท้ของ (state: Object) ที่คืนค่าอ็อบเจ็กต์ที่คำนวณจากสถานะ Redux วัตถุนี้จะถูกรวมเข้ากับอุปกรณ์ประกอบฉากที่ส่งไปยังส่วนประกอบที่ถูกห่อ สิ่งนี้เรียกอีกอย่างว่าตัวเลือก เนื่องจากมันเลือกส่วนต่าง ๆ ของสถานะ Redux เพื่อรวมเข้ากับอุปกรณ์ประกอบฉากของส่วนประกอบ

mapDispatchToProps ยังเป็นฟังก์ชันบริสุทธิ์ แต่หนึ่งใน (dispatch: (Action) => void) ที่ส่งคืนอ็อบเจ็กต์ที่คำนวณจากฟังก์ชันการจัดส่ง Redux ออบเจ็กต์นี้จะถูกรวมเข้ากับอุปกรณ์ประกอบฉากที่ส่งไปยังส่วนประกอบที่ถูกห่อด้วยเช่นกัน

ตอนนี้เพื่อใช้ส่วนประกอบคอนเทนเนอร์ของเรา เราต้องใช้ส่วนประกอบ Provider ใน React-Redux เพื่อบอกส่วนประกอบคอนเทนเนอร์ว่าจะใช้ที่จัดเก็บใด:

 import { Provider } from 'react-redux' import { render } from 'react-dom' import store from './store' // where ever your Redux store resides import HelloWorld from './HelloWorld' render( ( <Provider store={store}> <HelloWorld /> </Provider> ), document.getElementById('container') )

องค์ประกอบของ Provider เผยแพร่ร้านค้าลงไปที่ส่วนประกอบย่อยที่สมัครรับข้อมูลจากร้าน Redux โดยเก็บทุกอย่างไว้ในที่เดียว และลดจุดเกิดข้อผิดพลาดหรือการกลายพันธุ์!

สร้างความมั่นใจในการโค้ดด้วย Redux

ด้วยความรู้ที่เพิ่งค้นพบของ Redux ไลบรารีที่รองรับจำนวนมากและการเชื่อมต่อเฟรมเวิร์กกับ React.js คุณสามารถจำกัดจำนวนการกลายพันธุ์ในแอปพลิเคชันของคุณได้อย่างง่ายดายผ่านการควบคุมสถานะ ในทางกลับกัน การควบคุมสถานะที่แข็งแกร่งจะช่วยให้คุณเคลื่อนไหวได้เร็วขึ้น และสร้างฐานรหัสที่มั่นคงด้วยความมั่นใจมากขึ้น