Immutability và Immutable.js trong ReactJs

Khi mới bập bõm vào làm với Laravel-ReactJs, có những keyword mà mình chưa từng được nghe bao giờ. Buồn một chút chính là Immutability nói chung và Immutable.js nói riêng khi sử dụng với ReactJs là một trong số đó (yaoming). Sau một hồi suy nghĩ sẽ gõ gì để túm lược lại khoảng thời gian vừa rồi thì cuối cùng mình đã quyết định những bài kiến thức cơ bản về ReactJs sẽ dành cho lần sau, và đâm luôn vào Immutability cho nóng 😃)

Immutability là gì?

Có thể hiểu đó là tính bất biến của đối tượng. Sau khi đã khởi tạo thì sẽ không thể thay đổi trạng thái. Nghe thì lạ lạ nhưng thực ra trước đây khi làm với mô hình MVC thì Mutability được sử dụng liên tục nhưng Immutability có lẽ được biết đến nhiều hơn ở Functional Programing.

Rõ hơn, mình sẽ so sánh việc khởi tạo Mutable Class với Immutable Class trong PHP:

class MutableClass
{
    private $paramA;
    
    public function __construct($paramA)
    {
        $this->paramA = $paramA;
    }
 
    public function getParamA()
    {
        return $this->paramA;
    }
    
     public function setParamA($newParamA)
    {
        $this->paramA = $newParamA;
    }
}
class ImmutableClass
{
    private $paramA;
    
    public function __construct($paramA)
    {
        $this->paramA = $paramA;
    }
 
    public function getParamA()
    {
        return $this->paramA;
    }
    
     public function changeState()
    {
        $paramA = $this->paramA;
        $newParamA = $paramA + 5;
 
        return new ImmutableClass($newParamA);
    }
}

Có thể dễ dàng nhận ra sự khác biệt ở đây chính là MutableClass có setParamA() để có thể trực tiếp thay đổi giá trị của paramA còn ở ImmutableClass đã không còn và thay vào đó là changeState() để trung gian thực hiện công việc đó mà không ảnh hưởng đến trạng thái của Class cũ.

Hay ví dụ về Immutability trong Javascript:

let x = 3;
let y = x;
console.log(y === x) // true

x = 5;

console.log(y === x) // false
console.log(y) // 3
console.log(x) // 5

Tại sao lại là Immutability?

Trong lập trình hướng đối tượng, sử dụng Mutability cho khả năng tự do thay đổi cấu trúc dữ liệu ban đầu của bạn và do đó cập nhật thay đổi cho tất cả các đối tượng khác có liên quan đến nó có thể làm cho việc cập nhật cấu trúc dữ liệu dễ dàng hơn, nhưng đồng thời có thể dẫn tới nhiều khó khăn trong việc quản lý và fix bug. Nếu sử dụng Immutability, bạn luôn tạo ra một bản sao của cấu trúc dữ liệu cũ và áp dụng các thay đổi cho bản sao thay vì thay đổi cấu trúc dữ liệu ban đầu. Và tác dụng lớn nhất của việc sử dụng Immutability đó chính là khiến cho việc lập trình trở nên đơn giản hơn :love_you_gesture:

Immutability trong React-Redux

Trong React-Redux, rất nhiều components khác nhau làm việc với state chung của ứng dụng và các components đó hoạt động cùng lúc nên sử dụng Immutability với state là cách tốt nhất để theo dõi hoạt động của nó.

Dưới đây là một ví dụ về sử dụng Immutability trong React-Redux:

// Action type
const GET_STUDENT = 'GET_STUDENT';
const ADD_STUDENT = 'ADD_STUDENT';

// Reducer
const studentInitialState = {
    students: [],
    slelectedStudent: {}
};

function studentData (state = studentInitialState, action) {
    const newState = Object.assign({}, state);
    // Tạo ra bản copy của state cũ, thay vì sử dụng Mutable state
    
    switch (action.type) {
        case GET_STUDENTS:
            newState.students = action.students
        case ADD_STUDENT:
            newState.students = [...state.students, action.student];
        default:
            return state;
    }
    return newState;
}

Để GET_STUDENTS hoặc ADD_STUDENT, mình tạo một newState và thực hiện những thay đổi bằng cách sử dụng Object.assign và cuối cùng trả về newState chứ không phải là state cũ để có thể đảm bảo trạng thái của nó.

Vấn đề xảy ra khi ta có quá nhiều state lồng nhau, trong đó lại có nhiều Object và Array lồng nhau nữa thì cách sử dụng [..., state.students] sẽ không ổn nữa (omg). Vì vậy, mình đề cập đến Immutable.js ở trên là để giải quyết những chuyện này.

Immutability với Immutable.js

Đây là một thư viện được Facebook xây dựng để làm việc cùng React, nó sử dụng Structural Sharing: Thay vì phải chạy lung tung để có thể cập nhật value từ 3 -> 14 cho tea, chúng ta tạo ra những root copy mới để chia sẻ và cập nhật một cách đơn giản hơn. Nó có thể cài đặt qua npm bằng cách:

npm install immutable

Ví dụ ở trên được viết lại như sau:

import Immutable from 'immutable';

// Action type
const GET_STUDENT = 'GET_STUDENT';
const ADD_STUDENT = 'ADD_STUDENT';

// Reducer
const studentInitialState = Immutable.fromJS({
    students: [],
    slelectedStudent: {}
});
// .fromJS() sẽ lồng array & object vào trong Immutable Maps & Lists  
export default function studentData (state = studentInitialState, action) {
    
    switch (action.type) {
        case GET_STUDENTS:
            return state.set('students', Immutable.fromJS(action.students));
            // hoặc return state.set('students').toList();
        case ADD_STUDENT:
            return state.update('students', students => students.push(Immutable.fromJS(action.student)));
        default:
            return state;
    }
}

Ngoài ra Immutable.js có rất nhiều method hay ho khác mà các bạn có thể tìm được ở Docs chính thống của nó.

Kết bài

Trên đây là những kiến thức mình tổng hợp lại để nắm rõ hơn về Immutabilty. Mong chúng sẽ giúp ích ít nhiều cho các bạn. Cám ơn đã đọc bài viết của mình 😁

Nguồn trong bài:

https://medium.com/@yej.arin.choi/this-is-a-post-that-summarizes-my-dive-into-immutability-in-programming-what-it-is-why-its-34cbba44f889 https://medium.com/@dtinth/immutable-js-persistent-data-structures-and-structural-sharing-6d163fbd73d2 http://facebook.github.io/immutable-js/docs/#/