2
Hãy xây dựng một ứng dụng không tầm thường với React và sau đó cấu trúc lại nó để sử dụng Redux!

Phần lớn  lời khuyên  bạn nhận được về việc bổ sung Redux vào các dự án React của bạn là chỉ làm như vậy một khi chúng đạt đến một kích thước nhất định, vì sự phức tạp thêm mà Redux thêm vào. Điều đó chắc chắn công bằng. Nhưng nó sẽ để lại cho bạn một chút nợ kỹ thuật (tái cấu trúc sẽ được thực hiện sau) mà bạn sẽ không có nếu bạn mới bắt đầu với React và Redux.

Do đó, tôi nghĩ rằng có thể tốt khi trình bày một bài tập mà chúng tôi làm như vậy: xây dựng một ứng dụng đơn giản nhất có thể bằng cách sử dụng React và ReactDOM một mình (thậm chí không phải là JSX vì bạn cần thêm phụ thuộc và quá trình xây dựng để hỗ trợ điều đó), và sau đó tái cấu trúc để sử dụng JSX và Redux.

Trong  một bài trước , tôi đã mô tả dự án trò chuyện đa máy chủ dựa trên Node của tôi, có ứng dụng khách Javascript thô sơ được tích hợp. Mặc dù tất cả các chức năng hệ thống có thể được kiểm tra với một số phiên bản của máy khách đó, nhưng nó thiếu một số cách :

  • Nó chỉ cho phép bạn kết nối với hai người dùng được xác định trước (cái này cung cấp đầu vào tên người dùng miễn phí, vì vậy bất kỳ số nào cũng có thể kết nối).

  • Nó không hiển thị lịch sử tin nhắn có thể cuộn, mặc dù bạn có thể thấy tin nhắn mới nhất được gửi hoặc nhận trên một dòng.

  • Nó không hiển thị cho bạn danh sách tất cả người dùng khác được kết nối với lưới máy chủ, nó chỉ ghi lại danh sách người dùng vào bảng điều khiển mỗi khi máy chủ cập nhật máy khách.

  • Nó không thông báo cho bạn khi người nhận đã chọn của bạn bị ngắt kết nối hoặc kết nối lại.

  • Nó không hỗ trợ các luồng thông báo (ứng dụng khách này tự động hiển thị luồng phù hợp và chọn người nhận sau khi nhận được tin nhắn đến).

Và tối thiểu như vậy, khách hàng đó đã ở ngay rìa của sự phức tạp nơi một khuôn khổ của một loại nào đó sẽ được chào đón. Vì vậy, trong bài đăng này, tôi sẽ xây dựng một  ứng dụng khách đầy đủ tính năng hơn  để giao tiếp với cùng một máy chủ trò chuyện.

Cắt đầu tiên: Phản ứng và ReactDOM

Trong dự án React tối giản nhất, bạn chỉ cần kéo React và ReactDOM, sau đó sử dụng  React.createElement() để kết xuất các thành phần của bạn (mở rộng React.Component). Bạn có thể thấy phiên bản đầu tiên của dự án trên  thẻ 1.1.0 của nó .

Trong phiên bản đó của dự án, mẫu HTML trông như thế này:

Reac-chat-client.html

<!DOCTYPE html>
<html>
<head>
    <title>React Chat Client</title>
    <script src="https://unpkg.com/react@16.4.1/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@16.4.1/umd/react-dom.development.js"></script>
    <script src="https://cdn.socket.io/socket.io-1.4.5.js"></script>
</head>
<body>
<div id="app"></div>
<script type="module" src="/main.js"></script>
</body>
</html>

Lưu ý cách nó sử dụng các thẻ script để lấy các phụ thuộc của nó, React, ReactDOM và Socket.io, từ các URL CDN và tệp main.js là điểm truy cập vào ứng dụng. Điều này cho phép chúng tôi phục vụ ứng dụng với một máy chủ Express đơn giản như thế này:

client-server.js

// Serve the client
const express = require('express');
const app = express();
const path = require('path');
const port = 8080;

console.log(`Server for React instant message client started. http://localhost:${port}/`);
app.use(express.static('app'));

app.get('/', function(req, res) {
    console.log('New client request');
    res.sendFile(path.join(__dirname + '/react-chat-client.html'));
});

app.listen(port);

Điều đó vượt qua rất nhiều ma sát tinh thần liên quan đến thiết lập React toolchain. Không cần tạo ứng dụng phản ứng, Babel, Webpack, v.v.

Cấu trúc ứng dụng

Trong lần cắt đầu tiên này, mã ứng dụng chỉ bao gồm bốn tệp, được giấu trong một thư mục.

Bạn đã nhìn thấy hai người bọn họ ở trên,  client-server.js  và  phản ứng-chat-client.html . Hai cái còn lại là:

  • main.js

  • client.js

Main.js

Tệp này không thể đơn giản hơn và chỉ hiển thị lớp Máy khách dưới dạng phần tử DOM thay thế phần tử 'ứng dụng' trong mẫu HTML. Nhớ lại nó đã được đề cập trong một thẻ script trong phần thân của mẫu.

import { Client } from './Client.js'

function render() {
    ReactDOM.render(
        React.createElement(Client),
        document.getElementById('app')
    )
}
render();

Client.js

Cái này là nguyên khối, nặng chính xác sáu trăm dòng. Tôi sẽ không sao chép nó ở đây, nhưng nó đáng để xem  trong repo . Các lớp nguyên khối rất khó để duy trì và lý do, đặc biệt nếu có nhiều hơn một người trong nhóm.

Tệp này định nghĩa tất cả các giao thức, kiểu là CSS-in-JS, v.v. trong các hằng số mà tất cả các thành phần trong tệp có thể nhìn thấy. Nó cũng định nghĩa tất cả các thành phần, và, không cần nâng, chúng phải được xác định trước khi chúng được tham chiếu, vì vậy thành phần máy khách được xác định cuối cùng và tất cả các thành phần khác đến trước nó, v.v., với các thành phần lồng nhau. Yêu cầu đặt hàng không rõ ràng này là một lý do khác khiến các tệp nguyên khối là xấu.

React.createEuity vs JSX

Một điều tôi sẽ chỉ ra ở đây là làm thế nào, mặc dù có thể thực hiện React mà không cần JSX, các phương thức kết xuất của bạn trông như thế này:

    render() {
        return createElement('div', {style: clientStyle},

            // User selector
            createElement(UserInput, {
                connected: this.state.connected,
                onChange: this.onUserChange
            }),

            // Port selector
            createElement(PortSelector, {
                connected: this.state.connected,
                onChange: this.onPortChange
            }),

            // Recipient selector
            createElement(RecipientSelector, {
                users: this.state.users,
                recipient: this.state.recipient,
                onChange: this.onRecipientChange
            }),

            // Outgoing message input and send button
            createElement(MessageTransport, {
                connected: this.state.connected,
                recipient: this.state.recipient,
                outgoingMessage: this.state.outgoingMessage,
                onChange: this.onMessageInputChange,
                onSend: this.onSendMessage
            }),

            // Message History
            createElement(MessageHistory, {
                user: this.state.user,
                messages: this.state.messages,
                connected: this.state.connected
            }),

            // Footer (status line / connection toggle)
            createElement('div', {style: footerStyle},

                // Status Line
                createElement(StatusLine, {
                    status: this.state.status,
                    isError: this.state.isError
                }),

                // Connect button
                createElement(ConnectButton, {
                    enabled: (this.state.port && this.state.user),
                    connected: this.state.connected,
                    handleClick: this.onToggleConnection
                })
            )
        )
    }

thay vì phiên bản này, từ  phiên bản 1.1.0  đã thêm JSX:

   render() {
        return <div style={clientStyle}>

            <UserInput connected={this.state.connected} onChange={this.onUserChange}/>

            <PortSelector connected={this.state.connected} onChange={this.onPortChange}/>

            <RecipientSelector users={this.state.users}
                               recipient={this.state.recipient}
                               onChange={this.onRecipientChange}/>

            <MessageTransport connected={this.state.connected}
                              recipient={this.state.recipient}
                              outgoingMessage={this.state.outgoingMessage}
                              onChange={this.onMessageInputChange}
                              onSend={this.onSendMessage}/>

            <MessageHistory user={this.state.user}
                            messages={this.state.messages}
                            connected={this.state.connected}/>

            <Footer status={this.state.status}
                    isError={this.state.isError}
                    connectEnabled={(this.state.port && this.state.user)}
                    connected={this.state.connected}
                    handleToggle={this.onToggleConnection}/>

        </div>;
    }

LƯU Ý:  Đây là một chút cải tiến, nhưng tôi đảm bảo với bạn rằng chúng tôi sẽ   cải thiện đáng kể chức năng kết xuất đó vào cuối Phần 2.

Dòng dữ liệu

Tiền đề cơ bản của luồng dữ liệu trong phiên bản không có Redux này là thành phần Máy khách cấp cao nhất khởi tạo tất cả trạng thái trong hàm tạo của nó:

        this.state = {
            connected: false,
            status: 'Select a user and port.',
            isError: false,
            user: null,
            recipient: NO_RECIPIENT,
            outgoingMessage: '',
            messages: [],
            port: PORTS[0],
            users: []
        };

và sau đó chuyển các bit trạng thái và các cuộc gọi lại sửa đổi nó xuống các thành phần con dưới dạng đạo cụ, như vậy:

            // User input field
            createElement(UserInput, {
                connected: this.state.connected,
                onChange: this.onUserChange
            }),

Trong trường hợp này, cuộc  onUserChange gọi lại được chuyển đến   ConnectButton thể hiện như sau:

   // The user input field has changed
    onUserChange(user) {
        this.setState({user: user});
    }

Vì vậy, khi  UserInput trường nhập văn bản của thành phần thay đổi, kích hoạt lệnh gọi  onUserChange, thành phần Máy khách sẽ thay đổi trạng thái, khiến React hiển thị lại.

Điều đó đủ dễ dàng và thật tuyệt khi React đảm nhiệm việc kết xuất lại khi trạng thái thay đổi. Nhưng trong thế giới thực, cách tiếp cận này không có quy mô. Các thành phần cần phải đi vào các tệp của riêng chúng, JSX thực sự dễ dàng hơn cho chúng ta khi làm việc và hầu hết các nhóm như một số biến thể của mẫu Flux để quản lý trạng thái rộng của ứng dụng, bởi vì việc mô đun hóa và mở rộng khi dự án phát triển dễ dàng hơn.

Đó là tất cả cho Phần 1. Bây giờ, chuyển sang Phần 2 , nơi chúng tôi tái cấu trúc để sử dụng Redux và các ràng buộc!

Ở đây một lần nữa, để tham khảo, là phần cắt đầu tiên của dự án:

|