React dành cho AngularJS developer (Phần 2)

Tiếp nối bài viết trước, hôm nay chúng ta cùng tiếp tục xem xét cách giải quyết vấn đề về Component, Data binding,… trong React và Angular.

Component

React component thì có dạng giống như Angular directive, chúng được sử dụng để trừu tượng các cấu trúc DOM phức tạp, và chia nhỏ theo hành vi để có thể tái sử dụng. Ví dụ, để làm một slide show với Angular.

// index.html
<div ng-controller="SlideShowController">
  <slide-show slides="slides"></slide-show>
</div>

// slide-show.html
<div class="slideshow">
  <ul class="slideshow-slides">
  <li ng-repeat="slide in slides" ng-class="{ active: $index == activeIndex }">
    <figure>
      <img ng-src="{{ slide.imageUrl }}" />
      <figcaption ng-show="slide.caption">{{ slide.caption }}</figcaption>
    </figure>
  </li>
  </ul>
  <ul class="slideshow-dots">
    <li ng-repeat="slide in slides" ng-class="{ active: $index == activeIndex }">
      <a ng-click="jumpToSlide($index)">{{ $index + 1 }}</a>
    </li>
  </ul>
</div>

// js
app.controller("SlideShowController", function($scope) {
  $scope.slides = [{
    imageUrl: "image.jpg",
    caption: "This is image"
  }, {
    imageUrl: "image.jpg",
    caption: "This is image"
  }];
});

app.directive("slideShow", function() {
  return {
    restrict: 'E',
    scope: {
      slides: '='
    },
    template: 'slide-show.html',
    link: function($scope, element, attrs) {
      $scope.activeIndex = 0;
      $scope.jumpToSlide = function(index) {
        $scope.activeIndex = index;
      };
    }
  };
});

Trong khi có với React, component sẽ được render bên trong component khác, trao đổi dữ liệu với nhau thông qua props.

let _slides = [{
  imageUrl: "image.jpg",
  caption: "This is image"
}, {
  imageUrl: "image.jpg",
  caption: "This is image"
}];

class App extends React.Component {
  render() {
    return <SlideShow slides={ _slides } />
  }
}

class SlideShow extends React.Component {
  constructor() {
    super()
    this.state = { activeIndex: 0 };
  }
  jumpToSlide(index) {
    this.setState({ activeIndex: index });
  }
  render() {
    return (
      <div className="slideshow">
        <ul className="slideshow-slides">
          {
            this.props.slides.map((slide, index) => (
              <li className={ classNames({ active: index == this.state.activeIndex }) }>
                <figure>
                  <img src={ slide.imageUrl } />
                  { slide.caption ? <figcaption>{ slide.caption }</figcaption> : null }
                </figure>
              </li>
            ))
          }
        </ul>
        <ul className="slideshow-dots">
          {
            this.props.slides.map((slide, index) => (
              <li className={ (index == this.state.activeIndex) ? 'active': '' }>
                <a onClick={ (event)=> this.jumpToSlide(index) }>{ index + 1 }</a>
              </li>
            ))
          }
        </ul>
      </div>
    );
  }
}

React component chứa state bên trong nó (this.state), bạn có thể modify bằng cách gọi this.setState({ key: value}). Bất cứ sự thay đổi nào liên quan tới state của component đều khiến nó tự render lại.

Event trong React nhìn thì có vẻ như old-school style, dạng như inline event handler như kiểu onClick hay tương tự. Nhưng đừng vì thế mà cảm thấy nó chuối, bởi vì thực ra React đang cố gắng gain performance từ những điểm này, việc này tạo ra high performance delegated event listeners.

Two-Way Binding

Angular sử dụng ng-model và $scope để tạo ra liên kết giữa các luồng dữ liệu được trao đổi qua lại giữa form element và các property của Javascript object bên trong controller.

// Javascript
app.controller("TwoWayController", function($scope) {
  $scope.person = {
    name: 'Hoang'
  };
});

// HTML
<div ng-controller="TwoWayController">
  <input ng-model="person.name" />
  <p>Hello {{ person.name }}!</p>
</div>

React không sử dụng mô hình này, thay vào đó sử dụng one-way data flow.

class OneWayComponent extends React.Component {
  constructor() {
    super()
    this.state = { name: 'Hoang' }
  }
  change(event) {
    this.setState({ name: event.target.value });
  }
  render() {
    return (
      <div>
        <input value={ this.state.name } onChange={ (event)=> this.change(event) } />
        <p>Hello { this.state.name }!</p>
      </div>
    );
  }
}

Tag trong đoạn code trên được gọi là controlled input, có nghĩa là bất cứ khi nào giá trị của controlled input bị thay đổi thì hàm render sẽ được gọi. Bản thân component được gọi là stateful bởi vì nó tự manage data của chính nó. Điều này thì không được recommend cho phần lớn các component, vì thế ý tưởng là đễ giữ cho component , ta sẽ trap đổi data thông qua props.

Thông thường, một stateful container component hay là controller view thường nằm trên top của cây DOM với hàng loạt các statless component con nằm bên dưới, để hiểu rõ hơn các bạn có thể tìm hiểu tại sao component nên có state ở đây https://facebook.github.io/react/docs/interactivity-and-dynamic-uis.html#what-components-should-have-state.

Sự linh hoạt

Với one-way down data flow, dữ liệu chỉ có một chiều duy nhất vì thế dễ dàng hơn cho chúng ta gọi các phương thức của component cha thông qua callback. Sự linh hoạt này khiến cho developer có khả năng control được rất nhiều thứ khi thực hiện refactor component để tạo cho các thành phần hiển thị trở nên đơn giản nhất. Nếu component đã được refactor không chứa bất cứ state nào, thì ta có thể chuyển thành pure function. Nghe thì có vẻ khó hiểu, nhưng các bạn có thể hình tượng qua đoạn code sau.

const OneWayComponent = (props)=> (
  <div>
    <input value={ props.name } onChange={ (event)=> props.onChange(event.target.value) } />
    <p>Hello { props.name }!</p>
  </div>
);

class ParentComponent extends React.Component {
  constructor() {
    super()
    this.state = { name: 'Hoang' };
  }
  change(value) {
    this.setState({name: value});
  }
  render() {
    return (
      <div>
        <OneWayComponent name={ this.state.name } onChange={ this.change.bind(this) } />
        <p>Hello { this.state.name }!</p>
      </div>
    )
  }
}

Điều này có vẻ là khá khó chịu đối với những người đã quen với việc sử dụng two-way data binding. Lợi ích của việc có nhiều thành phần nhỏ trong tầng hiển thị, mà các thành phần đó chỉ chấp nhận data đưa vào là props và render chúng như một hành động mặc định, và từ đó vì các component là vô cùng đơn giản nên có thể giảm thiểu được số lượng bug, càng ít code, càng ít bug mà hahaha. Điều này cũng ngăn cản việc UI bị bất ổn định, điều mà thường xảy ra khi dữ liệu được đặt và maintain ở nhiều nơi khác nhau.

Dependency Injection, Services, Filters

Ngày nay, việc quản lý dependency trong Javascript tiện lợi hơn bao giờ hết nhờ có Webpack hay Browserify. Dưới đây là so sánh việc sử dụng dependency trong Angular và React, với ES6 việc các câu lệnh trở nên gần gũi với các ngôn ngữ class based hơn nhiều.

// An Angular directive with dependencies
app.directive('myComponent', ['Notifier', '$filter', function(Notifier, $filter) {
  const formatName = $filter('formatName');
  // use Notifier / formatName
}]

// ES6 Modules used by a React component
import Notifier from "services/notifier";
import { formatName } from "filters";

class MyComponent extends React.Component {

  // use Notifier / formatName

}

Nói tóm lại

Tóm lại, nếu bạn đang gặp vấn đề với rendering performance, một vấn đề thường gặp khi sử dụng Angular trong application, bạn có thể boost performance bằng cách chuyển việc rendering sang sử dụng React. Cũng không quá phức tạp khi sử dụng nhiều library để giải quyết cùng một vấn đề trong một application.

Trong khi React và Angular giải quyết một số vấn đề giống nhau, nhưng cách giải quyết lại hoàn toàn khác nhau. React ủng hộ phương thức khai báo function khi mà các component là các pure function giúp hạn chế lỗi.

Qua bài viết này, hy vọng các bạn đã có những cái nhìn cơ bản về cách giải quyết vấn đề của React và Angular, cũng như có thể giúp các bạn switch context dễ dàng hơn.

Tạm biệt và hẹn gặp lại trong các bài viết tiếp theo.

Bài gốc: https://codeaholicguy.wordpress.com/2016/01/21/react-danh-cho-angularjs-developer-phan-2/

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Hoàng Nguyễn

36 bài viết.
525 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
92 35
Nhu cầu về Javascript developer hiện nay trong thị trường IT là rất lớn. Nếu bạn có kiến thức ở mảng này thì cơ hội nghề nghiệp cũng như thu nhập c...
Hoàng Nguyễn viết 4 năm trước
92 35
White
53 0
Trong quá trình đi làm, nhất là nếu làm frontend thì chắc chắn sẽ có một lúc nào đó các bạn bị Chrome (trình duyệt nói chung) chửi vô mặt những thứ...
Hoàng Nguyễn viết 2 năm trước
53 0
White
52 21
Microservices hiện đang nhận được rất nhiều sự chú ý: các bài viết, các blog, các cuộc thảo luận trên phương tiện truyền thông, trên mạng xã hội, v...
Hoàng Nguyễn viết gần 5 năm trước
52 21
Bài viết liên quan
White
12 11
Khi các bạn viết sử dụng AngularJS có thấy thắc mắc về phần làm thế nào để mình viết 1 function mà có thể sử dụng cho toàn bộ app của mình không? V...
My Mai viết hơn 5 năm trước
12 11
White
70 8
Tăng sức mạnh cho javascript với lodash Lần này mình sẽ giới thiệu 1 thư viện javascript vô cùng bá đạo có tên là "lodash]1]", có thể nói nó là LI...
Huy Hoàng Phạm viết gần 5 năm trước
70 8
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
36 bài viết.
525 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!