Helpex - Trao đổi & giúp đỡ Đăng nhập

ReactJS thậm chí còn mạnh mẽ hơn như thế nào nếu bạn sử dụng Flux

Gần đây tôi đã bắt đầu học ReactJS. Với một tài liệu rất tốt có sẵn trên GitHub , tôi thấy nó rất dễ học. Tôi đã tạo một ứng dụng mẫu trong ReactJS và nó đang hoạt động tốt !!

Với một số kinh nghiệm tương tự, tôi muốn bắt đầu bằng cách đề cập đến hai trong số những điểm nổi bật của nó:

  1. HTML và Javascript trong một tệp duy nhất giúp dễ bảo trì
  2. Phát triển theo hướng thành phần, trong đó DOM được chia thành các thành phần để làm cho nó có thể tái sử dụng và dễ dàng kiểm tra

Sau đó, tôi nghe nói về React với Flux và tôi rất tò mò muốn biết tại sao chúng ta cần Flux khi React tự nó hoạt động tốt . Tôi không có nhiều kinh nghiệm phát triển trong ReactJS, đó là lý do tại sao tôi không nhận ra sức mạnh của Flux. Háo hức học hỏi, tôi tự nhủ rằng hãy kéo vớ một chút, học Flux và cũng giúp đào tạo những người khác.

Tôi đã tạo một ứng dụng bằng ReactJS nhưng ban đầu không sử dụng Flux để hiểu các lợi ích bổ sung của nó như:

  1. Khả năng bảo trì
  2. Khả năng đọc
  3. Luồng dữ liệu một chiều

Để thấy điều tương tự trong thực tế, hãy cùng xem việc phát triển một ứng dụng trước tiên mà không sử dụng Flux và sau đó sử dụng Flux.

Hãy bắt đầu bằng cách tìm hiểu định nghĩa / kiến ​​trúc cơ bản của Flux.

Kiến trúc Flux

ReactJS thậm chí còn mạnh mẽ hơn như thế nào nếu bạn sử dụng Flux

Có bốn điểm chính đối với Flux

  1. Hành động: Các hành động  rất đơn giản vì họ chỉ cần nhận yêu cầu từ Chế độ xem và chuyển nó đến Người điều phối. Nó hoạt động như một người trung gian giữa View và Dispatcher.
  2. Điều phối viên: Điều độ viên  có trách nhiệm chuyển thông tin để lưu trữ. Nó thực hiện điều đó thông qua phương thức <this.dispatch> trong bộ điều phối.
  3. Cửa hàng:  Cửa hàng chơi với dữ liệu. Nó giao tiếp với máy chủ phụ trợ theo yêu cầu từ View.
  4. Chế độ xem:  Chế độ xem dùng để hiển thị thông tin. Nếu các lượt xem cần một số thông tin thì họ sẽ lấy nó từ Store và nếu nó cần thực hiện một số hành động hoặc cập nhật / thêm bất kỳ thông tin nào thì nó sẽ thông báo cho Action.Action gọi Dispatcher đến lượt nó sẽ lấy dữ liệu từ Store.

Giả sử chúng ta phải tạo một ứng dụng TODO. Nó sẽ là một ứng dụng trang đơn được chia thành các thành phần sau:

  • Tiêu đề
    • Đếm Todo
  • Biểu mẫu Todo
  • Những việc cần làm
    • Mục Todo

Trên trình duyệt, các thành phần sẽ được hiển thị như trong sơ đồ dưới đây:

ReactJS thậm chí còn mạnh mẽ hơn như thế nào nếu bạn sử dụng Flux

Hành vi mong đợi

  • Todo Count sẽ hiển thị tổng số các mục Todo.
  • Todo Form sẽ có một trường đầu vào.
  • Khi gửi biểu mẫu, Mục Todo mới sẽ được thêm vào Danh sách Todo và số lượng sẽ được tăng lên trong Todo Count.

Ứng dụng mà không cần sử dụng Flux

Hãy tạo một Lớp TodoItem sẽ hiển thị các mục việc cần làm riêng lẻ. Nó sẽ nhận thông tin của mục việc cần làm từ thành phần mẹ của nó:

var TodoItem = React.createClass({
    render: function() {
        return ( 
          <li> { this.props.user } - { this.props.task} </li>)
    }
})

Thành phần bên dưới là TodoList chịu trách nhiệm hiển thị tất cả các mục việc cần làm. Thành phần này nhận “dữ liệu” từ lớp cha của nó thông qua các đạo cụ. “This.props.data” là danh sách các mục lặp lại và gọi TodoItem mà chúng tôi đã tạo ở trên.

var TodoList = React.createClass({
    render: function() {
        var TodoTasks = this.props.data.map(function(todoItem) {
            return ( 
              < TodoItem user = { todoItem.user } task = {todoItem.task} key = { todoItem.id } />
            )
        })
        return ( 
          < div className = "todo list" >
            <ul> Todo List 
                     {TodoTasks} 
          </ul> 
          </div>
        )
    }
});

Thành phần TodoCount chịu trách nhiệm hiển thị số lượng các mục. Nó sẽ được đếm từ thành phần TodoHeader.

var TodoCount = React.createClass({
    render: function() {
        return ( 
          <div> { this.props.count } </div>)
    }
});

Thành phần TodoHeader hiển thị tiêu đề của một ứng dụng và nó gọi thành phần TodoCount để hiển thị tổng số mục.

var TodoHeader = React.createClass({
    render: function() {
        return ( 
          <nav>
            Header 
            < TodoCount count = { this.props.count}
            /> 
          </nav>
        )
    }
})

Dưới đây là thành phần TodoForm. Hãy tập trung vào điều tương tự vì nó tạo ra dữ liệu ngoài việc hiển thị.

Phương thức handleSubmit được gọi khi nút Gửi được nhấp và nó gọi phương thức TodoSubmit mà nó nhận được từ thành phần Ứng dụng.

Khi tôi làm việc này, tôi có câu hỏi:

  •      Tại sao chúng tôi làm theo cách này?
  •      Tại sao thành phần Ứng dụng gửi biểu mẫu mà không phải TodoForm?

Lý do là Thành phần ứng dụng là cha / mẹ của tất cả các thành phần. Và các luồng dữ liệu từ cha mẹ sang con và con không thể thay đổi trạng thái của Cha. Ngoài ra, không thể có bất kỳ luồng giao tiếp nào giữa anh chị em.

Bây giờ nếu TodoForm sẽ gửi biểu mẫu thì thông tin này sẽ được chuyển đến TodoList hoặc thành phần TodoHeader như thế nào?

Đó là lý do tại sao chúng tôi làm cho thành phần Ứng dụng chịu trách nhiệm gửi biểu mẫu.

var TodoForm = React.createClass({
    getInitialState: function() {
        console.log("inside todo form of initial")
        return { user: '', task: '' }
    },

    handleUserChange: function(e) {
        this.setState({ user: e.target.value })
    },

    handleTaskChange: function(e) {
        this.setState({ task: e.target.value })
    },

    handleSubmit: function(e) {
        e.preventDefault();
        this.props.onTodoSubmit({ user: this.state.user, task: this.state.task })
        this.setState({ user: '', task: '' })
    },

    render: function() {
        return ( 
          <form className = "todoform"  onSubmit = { this.handleSubmit } >
                  <input type = "text" placeholder = "your name" 
            value = { this.state.user } onChange = { this.handleUserChange } /> 
                  <input type = "text" placeholder = "your task" 
            value = { this.state.task }
              onChange = { this.handleTaskChange } />
                  <input type = "submit" value = "submit" />
          </form>
        )
    }
})

Thành phần ứng dụng là thành phần gốc của tất cả. Nó có các phương pháp mà chúng ta cần thảo luận.

loadDataFromServer - Nó sẽ tải dữ liệu từ máy chủ, nhưng trong ví dụ của chúng tôi, không có bất kỳ máy chủ nào nên chúng tôi đã mã hóa dữ liệu.

handleTodoSubmit - Phương thức này sẽ được gọi bởi TodoForm như được giải thích trong thành phần TodoForm.

Khi phương thức này được gọi thì nó sẽ thay đổi trạng thái với mục Todo mới được tạo, mục này sẽ kích hoạt hiển thị lại thành phần Ứng dụng và tất cả các thành phần con sẽ được cập nhật thông tin mới.

var TODO = React.createClass({
    getInitialState: function() {
        return { data: [] }
    },

    loadDataFromServer: function() {
        this.setState({
            data: [
              { id: 1, user: "Adam", task: "This is task1"},
              { id: 2, user: "Ricky",task: "This is task2"}
            ]
        })
    },

    componentDidMount: function() {
        this.loadDataFromServer()
    },

    handleTodoSubmit: function(todo) {
        todo.id = Date.now()
        var todos = this.state.data
        var newTodos = todos.concat([todo])
        this.setState({
            data: newTodos
        })
    },


    render: function() {
        return ( 
          <div className = "todo">
            <TodoHeader count = { this.state.data.length } /> 
            <TodoForm onTodoSubmit = { this.handleTodoSubmit } /> 
            < TodoList data = { this.state.data } /> 
          </div>
        )
    }
})

ReactDOM.render( < TODO /> , document.getElementById("example"))

Như bạn có thể thấy rằng nếu các thành phần anh chị em muốn giao tiếp với nhau thì chúng cần phải truyền dữ liệu cho thành phần mẹ của chúng.

Giống như trong ví dụ của chúng tôi, Todo Form muốn thông báo cho Todo List rằng mục mới đã được thêm vào.

Đó là lý do tại sao Thành phần ứng dụng đang chuyển lệnh gọi lại handleTodoSubmit. Vì vậy, khi đệ trình Biểu mẫu Todo, nó đang gọi phương thức handleTodoSubmit của thành phần Ứng dụng thông qua cuộc gọi lại. Và handleTodoSubmit đang cập nhật trạng thái của Ứng dụng gây ra kết xuất thành phần Ứng dụng bằng cách cập nhật Todo Item và Todo Header.

Trong ví dụ của chúng tôi, chỉ có 1 hệ thống phân cấp nhưng trong các tình huống thời gian thực, có thể có hệ thống phân cấp nhiều cấp trong đó con trong cùng muốn cập nhật cấp con trong cùng khác. Sau đó, bạn phải chuyển phương thức gọi lại trên tất cả các cấu trúc phân cấp.

Nhưng điều đó sẽ làm cho nó ít khả năng bảo trì hơn và nó cũng ảnh hưởng đến khả năng đọc theo những cách tiêu cực, dẫn đến mất sức mạnh của React.

Bây giờ chúng ta hãy thử cùng một ứng dụng này bằng cách sử dụng Flux

Như chúng ta đã thấy, có 4 lớp trong Flux. Hãy viết một đoạn mã theo cấu trúc của nó.

Hoạt động

Các hành động được gọi bởi Chế độ xem. Nếu View muốn cập nhật dữ liệu trong Store thì View sẽ thông báo về hành động thay đổi này. Ở đây chúng tôi đã tạo AppAction. Trong này, chỉ có 1 hành động tức là addItem.

Điều này sẽ được gọi khi chúng ta muốn thêm một Mục việc cần làm mới. Hành động này gọi thêm handleViewAction của điều phối viên (AppDispatcher).

var AppDispatcher = require('../dispatchers/app-dispatcher');
var AppAction = {
    addItem: function(item) {
        AppDispatcher.handleViewAction({
            actionType: 'ADD_ITEM',
            item: item
        })
    }
}

Điều phối

Trong AppDispatcher, handleViewAction được định nghĩa được gọi bởi AppAction. Trong hành động handleViewAction được truyền sẽ cho biết hành động nào được thực hiện.

this.dispatch bên trong handleViewAction, là một phương thức được xác định trước của Dispatcher. Phương thức này sẽ gọi nội bộ Cửa hàng.

var Dispatcher = require("flux").Dispatcher;
var assign = Object.assign;
var AppDispatcher = assign(new Dispatcher(), {
    handleViewAction: function(action) {
        console.log('action', action)
        this.dispatch({
            source: 'VIEW_ACTION',
            action: action
        })
    }
});

Cửa hàng

Chúng ta hãy thảo luận về các phương pháp của Store từng cái một.

dispatcherIndex - Việc thực thi bắt đầu từ đây vì AppDispatcher.register. Bất cứ khi nào phương thức điều phối của Dispatcher được gọi thì nó sẽ chuyển thông tin hành động đến bất cứ nơi nào AppDispatcher.register được định nghĩa.

Trong ví dụ của chúng tôi, chúng tôi chỉ có một hành động “ADD_ITEM” nhưng trong hầu hết các trường hợp, sẽ có nhiều hành động. Vì vậy, trước tiên chúng ta phải xác định loại hành động và dựa vào đó chúng ta sẽ thực hiện các hành động và sẽ gọi phương thức phát ra.

Ở đây, chúng ta đang gọi phương thức addTodoItem từ phương thức dispatcherIndex và sau đó là releaseChange.

addTodoItem - Trong phương pháp này, chúng tôi không làm bất cứ điều gì ngoại trừ việc đẩy mục todo mới vào mảng todoItems.

releaseChange - Trong releaseChange, chúng tôi đang sử dụng phương thức this.emit (CHANGE_EVENT), điều này sẽ cho người nghe của CHANGE_EVENT biết rằng có điều gì đó đã được thay đổi.

addListener - Phương thức này được Chế độ xem sử dụng để lắng nghe CHANGE_EVENT.

removeListener - Phương thức này được sử dụng bởi Chế độ xem để loại bỏ trình nghe.

getTodoItems - Phương thức này sẽ trả về tất cả các việc cần làm. Điều này sẽ được gọi bởi thành phần TodoList.

getTodoCount - Phương thức này sẽ trả về số lượng tất cả các việc cần làm. Điều này sẽ được gọi bởi thành phần TodoCount.

var AppDispatcher = require('../dispatchers/app-dispatcher');
var assign = Object.assign;
var EventEmitter = require('events').EventEmitter;
var CHANGE_EVENT = 'change';
var todoItems = [
  { id: 1, user: "Adam", task: "This is task1"},
  { id: 2, user: "Ricky", task: "This is task2"}
];

var AppStore = assign(EventEmitter.prototype, {
    emitChange: function() {
        this.emit(CHANGE_EVENT)
    },

    addListener: function(callback) {
        this.on(CHANGE_EVENT, callback)
    },

    removeChangeListener: function(callback) {
        this.removeListener(CHANGE_EVENT, callback)
    },

    dispatcherIndex: AppDispatcher.register(function(payload) {
        var action = payload.action;
        if (action.actionType == "ADD_ITEM") {
            this.addTodoItem(payload.action.item);
        }
        AppStore.emitChange();
        return true;
    }),

    getTodoItems: function() {
        return todoItems;
    },

    getTodoCount: function() {
        return todoItems.length;
    },

    addTodoItem: function(todo) {
        todoItems.push(todo);
    }
})

View / Components

Trong TodoTask, chúng tôi không thay đổi bất cứ điều gì, nó giống như trong ví dụ trên.

var TodoTask = React.createClass({
    render: function() {
        return ( 
          <li> { this.props.user } - { this.props.task } </li>
        )
    }
})

Trong thành phần TodoList, chúng tôi đang tìm nạp trực tiếp các mục cần làm từ Store bằng cách gọi AppStore.getTodoItems trong getInitialState.

Bây giờ câu hỏi là, "làm thế nào để thành phần này biết được khi nào vật phẩm mới được thêm vào?"

Câu trả lời là trong componentWillMount. Trong phương pháp này, chúng tôi đang gọi AppStore.addChangeListener sẽ lắng nghe sự kiện được định nghĩa trong addChangeListener của Store. Nếu có bất kỳ thay đổi nào thì nó sẽ gọi _onChange để thiết lập lại trạng thái.

var TodoList = React.createClass({
    getInitialState: function() {
        return { todoItems: AppStore.getTodoItems() }
    },
    componentWillMount: function() {
        AppStore.addChangeListener(this._onChange)
    },
    componentWillUnmount: function() {
        AppStore.removeChangeListener(this._onChange)
    },
    _onChange: function() {
        this.setState({ todoItems: AppStore.getTodoItems() })
    },
    render: function() {
        var TodoTasks = this.state.todoItems.map(function(todoTask) {
            return ( 
              < TodoTask user = { todoTask.user }
                task = { todoTask.task }
                key = { todoTask.id }
                />
            )
        })
        return ( 
          <div className = "todo list">
            <ul> 
               Todo List { TodoTasks } 
            </ul> 
          </div>
        )
    }
});

Tương tự như TodoList, TodoCount cũng sẽ lấy dữ liệu từ Store và có một trình lắng nghe được xác định trong componentWillMount.

var TodoCount = React.createClass({
    getInitialState: function() {
        return { count: AppStore.getTodoCount() }
    },
    componentWillMount: function() {
        AppStore.addChangeListener(this._onChange)
    },
    componentWillUnmount: function() {
        AppStore.removeChangeListener(this._onChange)
    },
    _onChange: function() {
        this.setState({ count: AppStore.getTodoCount() })
    },

    render: function() {
        return ( < div > { this.state.count } < /div>)
    }
});

Thành phần TodoHeader tương tự như ví dụ trên.

var TodoHeader = React.createClass({
    render: function() {
        return (
            <nav>
            Header 
            <TodoCount />
            </nav>
        )
    }
})

Trong TodoForm, khi gửi biểu mẫu, AppAction.addItem sẽ được gọi để cho Store biết thông qua Dispatcher như đã thảo luận ở trên.

var TodoForm = React.createClass({
    getInitialState: function() {
        return { user: '', task: '' }
    },

    handleUserChange: function(e) {
        this.setState({ user: e.target.value })
    },

    handleTaskChange: function(e) {
        this.setState({ task: e.target.value })
    },

    handleSubmit: function(e) {
        e.preventDefault();
        AppAction.addItem({ user: this.state.user, task: this.state.task })
        this.setState({ user: '', task: ''})
    },

    render: function() {
        return ( 
          <form className = "todoform" onSubmit = { this.handleSubmit} >
            < input type = "text"
            placeholder = "your name"
          value = {this.state.user} onChange = { this.handleUserChange } /> 
          < input type = "text" placeholder = "your task"
          value = {this.state.task} onChange = {this.handleTaskChange} /> 
          < input type = "submit" value = "submit" / >
          </form>
        )
    }
})

Bây giờ công việc còn lại của Application component chỉ là render 3 thành phần TodoHeader, TodoForm, TodoList.

var Application = React.createClass({
    render: function() {
        return ( 
            <div className = "todo" >
            < TodoHeader / >
            < TodoForm / >
            < TodoList / >
            </div>
        )
    }
})

ReactDOM.render( < Application / > , document.getElementById("example"))

Phần kết luận

Khi so sánh cả hai mã, bạn sẽ thấy rằng việc tạo ứng dụng mà không cần sử dụng Flux vẫn dễ dàng. Nhưng nếu bạn hiểu dòng chảy của Flux thì lựa chọn của bạn sẽ luôn là Flux vì dòng chảy của Flux sẽ giống nhau ngay cả khi ứng dụng của bạn ngày càng phức tạp. Nó cung cấp cho chúng tôi những lợi ích bổ sung về khả năng bảo trì, khả năng đọc, luồng dữ liệu một chiều .

Để tóm tắt, chúng ta hãy thử lại để hiểu quy trình.

Trong thành phần TODO, chúng tôi đang kết xuất 3 thành phần <TodoHeader>, <TodoForm>, <TodoList>. Không cần chuyển bất kỳ lệnh gọi lại nào, không cần xác định bất kỳ trạng thái nào vì thành phần <TODO> không thực hiện bất kỳ điều gì ngoại trừ việc gọi các thành phần khác.

Thành phần TodoHeader cũng không làm bất cứ điều gì ngoại trừ kết xuất hoặc gọi thành phần khác.

Trong thành phần TodoCount, chúng ta cần hiển thị số lượng tổng số mục. Chúng tôi sẽ tính từ AppStore về kết xuất của TodoCount. Nhưng số có thể nhận được bản cập nhật sau khi kết xuất thành phần TodoCount. Đó là lý do tại sao chúng tôi đã thêm trình xử lý trong componentWillMount và loại bỏ trong phương thức componentWillUnmount. Vì vậy, nếu có bất kỳ cập nhật nào trong Store liên quan đến số thì nó sẽ gọi phương thức _onChange và sẽ thay đổi trạng thái của số lượng tương ứng.

Thành phần TodoList đang hiển thị danh sách các Mục Todo và nó cần TodoItems. Hành vi tương tự như TodoCount về giao tiếp với AppStore.

Trong TodoForm, chúng tôi muốn gửi biểu mẫu và muốn cho các thành phần khác biết rằng mục mới đã được thêm vào. Vì vậy, khi gửi đi, thay vì nói với các thành phần khác, nó chỉ chuyển thông tin đến AppAction, nó sẽ gửi thông tin đến lưu trữ với sự trợ giúp của AppDispatcher. Và AppStore sẽ cập nhật / thêm dữ liệu.

Trong AppStore, có phương thức phát ra được gọi là trong EmbedChange, phương thức này sẽ cho phép người nghe biết rằng thông tin đã được cập nhật / bổ sung.

Như bạn thấy, các thành phần không phụ thuộc vào nhau. Nếu họ cần bất kỳ thông tin nào, họ sẽ lấy thông tin đó từ Store và nếu họ muốn cập nhật bất kỳ thông tin nào thì họ sẽ tạo một Hành động và nó sẽ được gửi cho Cửa hàng.

Rõ ràng, luồng dữ liệu là một chiều nên sẽ dễ hiểu và dễ bảo trì.

Hy vọng bạn thu được một cái gì đó từ bài viết này. Đề xuất / sửa chữa được hoan nghênh.

5 hữu ích 0 bình luận 8.8k xem chia sẻ

Có thể bạn quan tâm

loading