Reactjs - Bài 12 - Addon

Goal

Giới thiệu addon trong Reactjs

1. Addon

React.addons là nơi ta đóng gói những utils dành cho việc xây dựng React apps.

Cách sử dụng:

Thêm

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.3/react-with-addons.js"></script>

vào phần head của page.

Animation Addon.

React cung cấp addon component ReactTransitionGroup như là low-level API for animation, và một ReactCSSTransitionGroup để dễ dành implement những CSS animation và transition đơn giản.

Ví dụ

Lấy ví dụ trong bài ‘props and state’, ở ví dụ đó, ta có 1 list các Avatar. Khi click vào nút delete nào thì Avatar đó sẽ bị remove.

Bây giờ ta thử dùng Reactjs Animation Addon để tạo hiệu ứng khi ta remove một Avatar.

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv='Content-type' content='text/html; charset=utf-8'>
  <title>Basic Example Props</title>
  <style>
    .example-leave {
      opacity: 1;
      transition: opacity .5s ease-in;
    }

    .example-leave.example-leave-active {
      opacity: 0.01;
    }
  </style>
</head>
<body>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.3/react.js"></script>  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.3/JSXTransformer.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.3/react-with-addons.js"></script>
  <script type="text/jsx">

    var Avatar = React.createClass({
      propTypes: {
        id: React.PropTypes.number.isRequired,
        name: React.PropTypes.string.isRequired,
        width: React.PropTypes.number.isRequired,
        height: React.PropTypes.number.isRequired,
        initialLike: React.PropTypes.bool.isRequired,
        // Thêm Interface onDelete()
        onDelete: React.PropTypes.func.isRequired,
      },
      getInitialState() {
        return {
          liked: this.props.initialLike
        };
      },
      onClick() {
        this.setState({liked: !this.state.liked});
      },
      // Ủy quyền cho Component cha xử lý.
      _onDelete() {
        this.props.onDelete(this.props.id);
      },
      render() {
        var textLike = this.state.liked ? 'like' : 'haven\'t liked';
        return (
          <li key={this.props.id}>
            <span>{this.props.id}</span>
            <img  src={this.props.src} width={this.props.width} height={this.props.height} alt="alt" />
            <span>{this.props.name}</span>
            <button onClick={this.onClick}>{textLike}</button>
            <button onClick={this._onDelete}>Delete</button>
          </li>
        );
    }
    });
    var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
    var Avatars = React.createClass({

      getInitialState() {
        return {
          avatars: [
            {id: 1, name: "Avatar 1", height: 100, width: 100, initialLike: false, src: "http://canime.files.wordpress.com/2010/05/mask-dtb.jpg"},
            {id: 2, name: "Avatar 2", height: 100, width: 100, initialLike: true, src: "http://z4.ifrm.com/30544/116/0/a3359905/avatar-3359905.jpg"},
            {id: 3, name: "Avatar 3", height: 100, width: 100, initialLike: false, src: "http://www.dodaj.rs/f/O/IM/OxPONIh/134.jpg"}
          ]
        }
      },
      // Thêm method deleteItem() set lại State (chứa các Component con) cho Component cha này
      deleteItem(id) {
        this.setState({
          avatars: this.state.avatars.filter(function(avatar){
            return avatar.id !== id;
          })
        });
      },

      render() {  
        var avatars = this.state.avatars.map(function(avatar){
        // use below solution
        // map(function(){},this) 
        // or map(function(){}.bind(this)) 
        // or var that = this; onDelete = {that.deleteUser}
        // to pass this value to map function.
        // bind onDelete (event) to deleteUser.
          return <Avatar onDelete={this.deleteItem} id={avatar.id} name={avatar.name} width={avatar.width} height={avatar.height} src={avatar.src} initialLike={avatar.initialLike} />;
        }, this);
        return (
          <ul>
            <ReactCSSTransitionGroup transitionName="example">
              {avatars}
            </ReactCSSTransitionGroup>
          </ul>
        );
      }
    });

    var AvatarsComponent = React.render(<Avatars />, document.body);
  </script>
</body>
</html>

Các điểm updated:
1. Thêm react-with-addons.js
2. Thêm <style></style>
3. Wrap {avatars} bởi ReactCSSTransitionGroup

Trong component này, khi remove 1 item ra khỏi ReactCSSTransitionGroup nó sẽ lấy example-leave CSS class và example-leave-active CSS class được add vào trong ‘tick’ tiếp theo. Cái convention này dựa vào transitionName prop (Trong ví dụ trên ta có chỉ định transitionName=”example”).

2. Two-way binding

Phần này chủ yếu là tôi lược dịch lại từ trang gốc của Reactjs. Do đó nếu bạn cảm thấy nội dung trên trang gốc dễ hiểu hơn thì bạn nên bỏ qua bài này.

Chú ý: nếu bạn là Reactjs newbie thì bạn cũng không cần phải tìm hiểu về ReactLinkReactLink chưa cần thiết cho hầu hết các ứng dụng.

Trong Reactjs, data đi theo một luồng, theo mô hình input - output, ta gọi là one way binding.

Tuy nhiên, nhiều ứng dụng thường yêu cầu khi có lấy dữ liệu từ output và truyền ngược vào trong chương trình. Ví dụ như khi bạn muốn update React state từ input của user.

Trong React, để làm điều này, trước hết bạn phải listen sự kiện “change” ( onChange() ), rồi đọc giá trị của DOM và gọi setState() để thay đổi state của component. khi state của component thay đổi thì nó sẽ re-render lại UI của component.

Reactjs mang đến cho bạn addon ReactLink, sẽ luôn ràng buộc giữa giá trị của DOM và state của component. Hãy tưởng tượng, với ReactLink bạn đã có một sợi dây “kết nối” giữa data trên DOM và React state.

Chú ý: ReactLink thực chất cũng chỉ là một wrapper gộp onChange/setState(). Do đó về nguyên tắc nó không có thay đổi bản chất data flows trong React Application.

Phần ví dụ ReactLink: Before and After minh họa khá rõ về điều này, cho có lẽ tôi không cần trình bày thêm, chỉ lưu ý là ReactLink dùng mixins để thêm method linkState() từ LinkedStateMixin vào React component.

Trong bài trên, phần Under the Hood chủ yếu giải thích sâu hơn về LinkedStateMixin, nhưng tôi nghĩ trong giới hạn một newbie thì ta chỉ cần biết là React Addon có cung cấp LinkedStateMixin dành cho việt two-way binding. Còn nếu bạn muốn hiểu sâu hơn thì tôi nghĩ đọc source code react-with-addons.js phần LinkedStateMixin sẽ dễ hiểu hơn nhiều.

Ngoài 2 addon giới thiệu ở trên, Reactjs còn cung cấp các addon khác, bạn có thể tham khảo tại link bên dưới:

https://facebook.github.io/react/docs/addons.html