แตกต่างกับบทที่แล้วโดยสิ้นเชิง เนื้อหาในบทนี้จะง่ายขึ้นมาก ซึ่งเกี่ยวข้องกับการใช้งาน Redux ให้มีประสิทธิภาพมากขึ้น
แรกสุด เราจะทำการเพิ่ม Immutable JS เข้าไปในโค้ดเดิมของเรา Immutable นั้นเป็น library ที่ช่วยให้เราจัดการ, แก้ไข object ได้โดยไม่แก้ไข object ตรงๆ เช่น แทนที่จะทำแบบนี้
const obj = { a: 1 };
obj.a = 2; // เปลี่ยนแปลง `obj` ตัวเดิมเราจะทำแบบนี้แทน
const obj = Immutable.Map({ a: 1 });
obj.set('a', 2); // ได้ object ก้อนใหม่มา โดยที่ไม่ได้แก้ไขค่าใน `obj` ตัวเดิมตรงๆวิธีการแบบนี้ทำให้เราสามารถเขียนโปรแกรมในลักษณะของ functional programming ได้ ซึ่งจะเป็นประโยชน์มากเมื่อใช้หลักการเขียนโปรแกรมแบบนี้คู่กับ Redux เพราะถ้าดูดีๆ reducer function ของเรานั้นต้องเป็น pure function ที่ไม่ได้แก้ไข state ที่ถูกส่งมาเป็น parameter ตรงๆ แต่แปลงไปเป็น state object ก้อนใหม่แทน ซึ่งการใช้ Immutable จะช่วยงานเราในส่วนนี้ได้
- สั่ง
yarn add immutable
เราจะมีการใช้ Map ในโปรเจคของเรา แต่ใน ESLint และ Airbnb นั้นจะบ่นเราเรื่องของการใช้ตัวแปรชื่อพิมพ์ใหญ่ทั้งๆ ที่มันไม่ใช่ class ให้เราทำการเพิ่มค่าส่วนนี้เข้าไปใน package.json ในส่วนของ eslintConfig
"rules": {
"new-cap": [
2,
{
"capIsNewExceptions": [
"Map",
"List"
]
}
]
}นั่นจะทำให้เราใช้ Map กับ List (Immutable objects สองชนิดที่เรามักจะใช้แทบจะทุกครั้ง) ได้แล้ว โดยที่ ESLint มองข้ามกฎการตั้งชื่อเริ่มต้นด้วยตัวพิมพ์ใหญ่ไป
กลับมาที่เรื่องของ Immutable กัน ใน dog-reducer.js ให้แก้ไขโค้ดให้มีหน้าตาดังนี้
import Immutable from 'immutable';
import { MAKE_BARK } from '../actions/dog-actions';
const initialState = Immutable.Map({
hasBarked: false,
});
const dogReducer = (state = initialState, action) => {
switch (action.type) {
case MAKE_BARK:
return state.set('hasBarked', action.payload);
default:
return state;
}
};
export default dogReducer;ตอนนี้จะเห็นว่า state เริ่มต้นจะถูกสร้างมา โดยใช้ Immutable Map แล้ว และ state ใหม่จะถูกสร้างมาโดยใช้ function set() เผื่อหลีกเลี่ยงการเปลี่ยนแปลงค่าใน state เก่าโดยตรง
ในโค้ด containers/bark-message.js แก้ function mapStateToProps ให้ใช้ .get('hasBarked') แทนที่จะใช้ .hasBarked
const mapStateToProps = state => ({
message: state.dog.get('hasBarked') ? 'The dog barked' : 'The dog did not bark',
});แอพจะทำงานได้เหมือนเดิม ตามที่เราเคยทำในบทที่แล้ว
หมายเหตุ: ถ้า Babel แจ้งเรื่องเกี่ยวกับ Immutable มีขนาดเกิน 100KB ให้เพิ่ม "compact": false ในไฟล์ package.json ภายใต้ field babel
ดังที่เห็นใน code ด้านบน state object ของเรายังคงเก็บ เป็น plain object ที่มี dog เป็น attribute เหมือนเดิม ซึ่งไม่ใช่ immutable object ซึ่งเป็นเรื่องที่รับได้โดยปกติ แต่ถ้าหากเราต้องการให้ทุกอย่างถูกจัดการด้วยความเป็น immutable objects เท่านั้น คุณต้องใช้ package redux-immutable เพื่อแทนที่ function combineReducers ของ Redux ไปด้วย
ส่วนนี้จะทำหรือไม่ทำก็ได้ แต่หากอยากใช้ redux-immutable ก็ให้ทำตามนี้
- สั่ง
yarn add redux-immutable - แทน function
combineReducersในไฟล์app.jsxโดยใช้ package ที่ import มาจากredux-immutableแทน - ในไฟล์
bark-message.jsแทนstate.dog.get('hasBarked')ด้วยstate.getIn(['dog', 'hasBarked'])
เมื่อคุณเริ่มเพิ่ม actions เข้าไปในแอพมากขึ้น เราจะค้นพบว่าเรามักจะทำอะไรซ้ำซากคล้ายๆ เดิมเยอะเหลือเกิน package redux-actions ช่วยให้เราลดความซ้ำซากเหล่านั้นได้ ด้วยความช่วยเหลือของ redux-actions เราสามารถเขียนโค้ด dog-actions.js ให้บางลงได้เยอะ แบบนี้
import { createAction } from 'redux-actions';
export const MAKE_BARK = 'MAKE_BARK';
export const makeBark = createAction(MAKE_BARK, () => true);redux-actions เป็นหนึ่งในการ implement ของ Flux Standard Action model เหมือนๆ กับ action ที่เราเคยเขียนก่อนหน้านั้น ดังนั้นการใช้ redux-actions เหมือนให้เราเขียนโค้ดสั้นลง แต่ได้ผลเหมือนเดิม
- อย่าลืมที่จะสั่ง
yarn add redux-actionsเข้าไปด้วยก่อนแก้โค้ดนี้
บทถัดไป บทที่ 11 - การทำ Testing โดยใช้ Mocha, Chai และ Sinon