12/08/2018, 15:57

ReactJS Components: Learning the Basics

React là một thư viện UI tuyệt vời và khá được ưa chuộng. Tuy nhiên, có một vấn đề khiến nhiều người còn chần chừ khi bắt tay làm việc với React, đó là có quá nhiều options chỉ để giải quyết một việc. Đây cũng là 1 trong những thách thức của các lập trình viên khi làm việc với thư viện này. Có rất ...

React là một thư viện UI tuyệt vời và khá được ưa chuộng. Tuy nhiên, có một vấn đề khiến nhiều người còn chần chừ khi bắt tay làm việc với React, đó là có quá nhiều options chỉ để giải quyết một việc. Đây cũng là 1 trong những thách thức của các lập trình viên khi làm việc với thư viện này. Có rất nhiều cách để thiết lập và quản lý 1 React component, hàng tấn các công cụ mới nổi dùng cho việc quản lý state. Vậy thì lựa chọn nào là tối ưu, hay công cụ nào được chấp nhận nhiều nhất bởi cộng đồng? Hôm nay chúng ta sẽ cùng nhau phân tích. Trong bài viết này có tham khảo các topics viết về React hữu ích khác, đồng thời cũng có vài khái niệm hơi bối rối cho các bạn mới tìm hiểu, giả dụ như:

  • Component Ownership
  • Prop Validation
  • Component Interaction
  • Component Defaults
  • ES6+ Components
  • Stateless Components

Bài viết này rất cơ bản, vì thế có thể nhiều bạn đã biết rồi, nếu muốn tìm hiểu các bài nâng cao, các bạn có thể vào đây để tìm hiểu thêm: https://scotch.io/search?query=react

Với React, việc truyền nhận data chủ yếu xảy ra giữa các components và các service cung cấp data bên ngoài (HTTP, localStorage). Props có tính chất bất biến, chính vậy nên nó ko thể nhận được giá trị từ components cha truyền xuống và ko thể thay đổi được. Điều này đặt ra một thách thức, bởi vì đa số các app hiện nay ko dựa trên những trạng thái có sẵn khi load trang ban đầu. Và states sinh ra để đảm đương nhiệm vụ này. Khi khởi tạo React, chúng ta định nghĩa một initial state và giữ cho state đó đồng bộ với props. Khi state cập nhật giá trị, props có thể dễ dàng đồng bộ được:

var Counter = React.createClass({
    getInitialState: function() {
    return {
        counter: 0
    };
  },
  render: function() {
    return <div>
            <h2>{this.props.name}</h2>
            {this.state.counter}
      </div>;
  }
});

ReactDOM.render(
  <Counter name={'Counter'} />,
  document.getElementById('container')
);

Ví dụ trên vẫn còn khá chuối vì theo như code bên trên thì props hoàn toàn có thể thay thế được vị trí của thằng state. Trong khi đi sâu hơn nữa, chúng ta sẽ thấy rõ lý do cho sự tồn tại của state.

Điều này rất dể hiểu. Nếu 1 component A render 1 component B trong method render của nó, thì thằng A được gọi là boss. Thằng boss này sở hữu component B và có quyền điều khiển thằng B. Có thể gọi điều này là parenting .

Hãy cùng xem một ví dụ khác:

var CounterDisplay = React.createClass({
    render: function(){
    return <div>{this.props.counterProp}</div>
  }
})

var Counter = React.createClass({
    getInitialState: function() {
    return {
        counter: 0
    };
  },
  render: function() {
    // Child component rendered
    // React will throw an error if the the DOM doesn't have a single parent
    return <div>
            <h2>{this.props.name}</h2>
            <CounterDisplay counterProp={this.state.counter}></CounterDisplay>
      </div>;
  }
});

ReactDOM.render(
  <Counter name={'Counter'} />,
  document.getElementById('container')
);

Counter đang render một component khác là CounterDisplay. Counter có trách nhiệm quản lý và đồng bộ props của CounterDisplay. Bạn có thể thấy cách chúng ta truyền state xuống component con thông qua props.

Nếu chúng ta có 1 button (hoặc nhiều hơn) trong một component con có thể tăng, giảm và được quản lý bởi component cha. Ta sẽ làm gì?

Sự tương tác giữa các React components diễn ra theo 2 hình thức : Dòng data truyền từ component cha xuống con và ngược lại. Chúng ta đã thấy việc truyền data từ cha xuống con như thế nào thông qua props rồi. Để truyền data từ component con lên, ta xử lý thông qua props của component cha:

var CounterDisplay = React.createClass({
    render: function(){
    // Calls the handler props once events are fired
    return <div>
            <div>{this.props.counterProp}</div>
        <button onClick={this.props.incrementCounter}>+</button>
        <button onClick={this.props.decrementCounter}>-</button>
        </div>
  }
})

var Counter = React.createClass({
    getInitialState: function() {
    return {
        counter: 0
    };
  },
  handleIncrement(){
    // Update counter state
    this.setState({counter : this.state.counter+1});
  },
  handleDecrement(){
      // Update counter state
    this.setState({counter : this.state.counter-1});
  },
  render: function() {
    // Pass down handlers to CounterDisplay component
    return <div>
            <h2>{this.props.name}</h2>
            <CounterDisplay 
            counterProp={this.state.counter}
          incrementCounter={this.handleIncrement}
          decrementCounter={this.handleDecrement}></CounterDisplay>
      </div>;
  }
});

ReactDOM.render(
  <Counter name={'Counter'} />,
  document.getElementById('container')
);

CounterDisplay component có sự kiện click. Handler ko thấy trong component này, hơn nữa, handlers được xử lý bởi component cha là counter. Handler lần lượt cập nhật state thông qua method this.setState() và counter nhận được nó.

Không chỉ states có nhiệm vụ khởi tạo giá trị với method getInitialState. Nếu cần thiết, bạn có thể thiếp lập giá trị mặc định cho props. Để làm điều này, bạn có thể sử dụng method getDefaultProps:

getDefaultProps: function() {
     return {
       name: 'Counter'
     };
},

Việc này rất hữu ích cho việc setup giá trị mặc định trong ứng dụng.

Validation giúp chúng ta yên tâm hơn về dữ liệu mà user nhập vào.

var CounterDisplay = React.createClass({
    render: function(){
    // Calls the handler props once events are fired
    return <div>
            <div>{this.props.counterProp}</div>
        <br />
        <button onClick={this.props.incrementCounter}>+</button>
        <button onClick={this.props.decrementCounter}>-</button>
        </div>
  },
  // Setup validation for each props
  propTypes: {
    // Must be a number
    counterProp: React.PropTypes.number.isRequired,
    // Must be functions
    incrementCounter: React.PropTypes.func.isRequired,
    decrementCounter: React.PropTypes.func.isRequired
   }
})

Nếu tất cả bạn cần chỉ là quan tâm đến việc type của nó là gì mà ko quan tâm đến chuyện nó có tồn tại hay ko, bạn có thể bỏ qua isRequired:

propTypes: {
    // Should be a number
    counterProp: React.PropTypes.number,
    // Should be functions
    incrementCounter: React.PropTypes.func,
    decrementCounter: React.PropTypes.func
   }

Với ES6, bạn có thể sử dụng classes để khởi tạo React components.

// Extends React.Compoent
class Comment extends React.Component {
 // Render method now a class member rather than
 // object property
  render(){
    return <h1>{this.props.name}</h1>;
  }
}

 React.render(<Comment name={'Comment'}/>, document.getElementById('container'));

Tên component là tên của class và class kế thừa các functions từ React.Component.

Setting State with Classes

class Comment extends React.Component {
   constructor(props) {
        super(props);
        this.state = {
            counter: 0
        };
  }
  render(){
    return <h1>{this.props.name}</h1>;
  }
}

 React.render(<Comment name={'Comment'}/>, document.getElementById('container'));

Initial state bây giờ được thiết lập trong constructor của class thay vì dùng getInitialState. Có một điều quan trọng cần chú ý là việc truyền props ngược lại component cha băng cách sử dụng super(props).

Default Props and Validation with Class

// Validation
Comment.propTypes = {
        counterProp: React.PropTypes.number.isRequired,
    incrementCounter: React.PropTypes.func.isRequired,
    decrementCounter: React.PropTypes.func.isRequired
};
// Defaults
Comment.defaultProps = {
    name: 'Counter'
};

Khi một component ko cần đến state, nó ko thích hợp để dùng class định nghĩa component. Bạn có thể dùng function nếu tất cả những gì được yêu cầu trong component chỉ là props:

function CommentDisplay(props) {
  return <div>
            <div>{props.counterProp}</div>
        <br />
        <button onClick={props.incrementCounter}>+</button>
        <button onClick={props.decrementCounter}>-</button>
        </div>
}

Rất đơn giản phải không nào?

Chủ đề chúng ta đang nói đến khá thiết thực trong việc build một app thực tế trong React. Hẹn gặp lại các bạn trong loạt bài sau. Nguồn: https://scotch.io/tutorials/reactjs-components-learning-the-basics

0