Một số khái niệm cần biết trong React



  • Giới thiệu

    Trong bài viết nay mình sẽ giới thiệu đến các bạn một số khái niệm quan trọng trong react. Đây là những khái niệm cần lắm rõ để trong quá trình phát triển với react tránh mắc phải việc mất thời gian cho những lỗi cơ bản mà mình đã gặp phải do chưa lắm vững kiến thức. Mình cũng ko lan man nữa. Phần đâu tiên là vòng đời của react component.

    Vòng đời trong React Component

    Khái niệm component lifecycle trong react là một trong những khái niệm quan trọng nhất, hiểu rõ lifecycle của component sẽ giúp bạn biết khi component được tạo, thay đổi, những method nào được gọi và làm những công việc gì. Để có thể hiểu rõ về lifecycle thì chúng ta cần nắm được sự khác nhau của lifecycle khi component được khởi tạo (mounting), khi state, props thay đổi(Updating) và khi unmount component như hình dưới đây:

    1. Mounting

    Trong giai đoạn nay sẽ bao gồm các phương thức khởi tạo state, props như sau :

    constructor(props) {
        super(props);
        this.state = {
             arrayIdea: [],
             textIdea: "",
             booleanIdea: false
        };
        // props sẽ được truyền từ class cha xuống
        console.log(props);
      }
    

    Tiếp theo phương thức componentWillMount sẽ được gọi ở đây và lưu ý hàm này chỉ được gọi 1 lần duy nhất trước khi thực hiện phương thức render().
    Phương thức render sẽ trả về nội dung mà bạn muốn hiển thị bao gồm code html, css. Dưới đây là ví dụ một hàm render

    render() {
        return (
          <div className="tile">
               <h2>Hello world</h2>
          </div>
        );
      }
    

    Ngay sau khi phương thức render được gọi thì phương thức componentDidMount sẽ được gọi. Tại đây khi mà mọi thứ đã được xây dựng hoàn chỉnh, ta có thể thực hiện các thao tác với component vừa mới tạo ví dụ: thực hiện các hàm bất đồng bộ để lấy dữ liệu, thao các với DOM.

    1. Updating

    Khi state thay đổi, hoặc props sẽ trigger đến những phương thức như sau:

    Trường hợp thay khi props thay đổi( từ lớp cha chuyền dữ liệu mới lớp con) để có thể so sánh giữa props mới với props cũ, hoặc đơn giản là cập nhật lại state từ props mới ta có thể xử lý ngay tại đây như sau:

    componentWillReceiveProps(nextProps) {
        this.setState({
          idea: nextProps.newIdea
        });
        //or showNotification()
      }
    

    Tiếp theo shouldComponentUpdate sẽ được thực hiện vả trả về kết quả true/false . Vậy ta có thể sử dụng để kiểm tra xem có thỏa mãn điều kiện ko nếu có thì trả về true để update còn không sẽ trả về false như sau:

    shouldComponentUpdate(nextProps, nextState) {
            console.log(nextState)
            if (someCondition) {
                return true;
            }
            return false;
        }
    

    Ngay sau khi shouldComponentUpdate return true thì componentWillUpdate sẽ được gọi:

    componentWillUpdate(nextProps, nextState) {
    // shouldComponentUpdate return true
    }
    

    Cuối cùng componentDidUpdate sẽ được gọi sau khi render xong.

    componentDidUpdate(prevProps, prevState) {
    
    }
    
    1. Unmounting

    Unmouting là khi component được xóa khỏi DOM.Chỉ có một method duy nhất được gọi trước khi Component được xóa khỏi DOM. Được sử dụng trong trường hợp muốn cleanup event hay timer chẳng hạn.

    Immutability Helpers

    Trong javascript làm việc với dữ liệu có dạng immutable rất là khó so với các ngôn ngữ khác. React cũng không phải ngoại lệ và chịu ảnh hường đến hiệu năng nếu ko giải quyết được việc xử lý immutable data nguyên nhân là do cơ chế render() như sau

    Mình cũng đã nói ở phần vòng đời của react component phương thức render() chỉ được gọi lại khi có sự thay đổi từ state hoặc props. Trong trường hợp nếu state trong class react có cấu trúc đơn giản thì việc so sánh sẽ không tốn kém nhưng tưởng tượng object của bạn có dạng như sau:

    this.state.myData.x.y.z = 7;
    // or...
    this.state.myData.a.b.push(9);
    

    Và bạn phải thực hiện lần lượt các thao tác sau để tạo new state

    const newData = deepCopy(this.state.myData);
    newData.x.y.z = 8;
    newData.a.b.push(10);
    ...
    function deepCopy(data) {}
    

    Việc xử lý như trên gây ra tốn kém về hiệu năng quá lớn đồng thời sẽ có những trường hợp ko thể làm như trên được và sinh bug cho hệ thống. Do đó React cung cấp cho ta một thư viện để xử lý việc deepCopy và thay đổi dữ liệu vừa được copy: Immutability Helpers

    Để sử dụng thư việc Immutability Helpers của react trong đường dẫn dự án ta sử dụng npm để cài đặt

    npm install immutability-helper --save
    

    Tiếp theo là các câu lệnh trong immutability-helper:

    • {$push: array} thêm tất cả items vào cuối array được chỉ định
    • {$unshift: array} thêm tất cả items vào đầu array được chỉ định
    • {$splice: array of arrays} với một array được truyền vào để thửc hiện remove phần tử trong array được chỉ định vào thay thế phần tử đó bằng số mảng khác
    • {$set: any} thay thế đối tượng chỉ định bằng đối tượng mới
    • {$merge: object} dùng cho đối tượng dangh hash (key: value)
    • {$apply: function}: áp dụng function cho đối tượng được chỉ định

    Ví dụ: mình có viết thử 1 đoạn để cập nhât state của component sử dụng immutability-helper

    import update from 'react-addons-update';
    import React from 'react';
    import ReactDOM from 'react-dom';
    
    class Hello extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          ideas: [{
            "id": 1,
            "title": "A new cake recipe",
            "body": "Made of chocolate"
          },
          {
            "id": 2,
            "title": "A twitter client idea",
            "body": "Only for replying to mentions and DMs"
          }]
        };
      }
    
      helperReact = () => {
        let index = 0;
        let newData = update(this.state.ideas, {
          [index]: {"title": {$set: "this is a new title"}}
        });
        this.setState({
          ideas: newData
        });
      }
    
      showState = () => {
        console.log(this.state.ideas);
      }
    
      componentDidMount() {
        this.helperReact();
      }
    
      render() {
        this.showState();
        return (
          <div>Hello {this.props.name}</div>
        );
      }
    }
    
    ReactDOM.render(
      <Hello name="World" />,
      document.getElementById('container')
    );
    

    Hoặc bạn có thể chạy thử trên các ứng dụng editor online: đây là ví dụ mình viết trên online editor. Ngoài ra bạn có thể tham khảo các ví dụ khác trên trang chủ của react

    AUTOBIND không còn hoạt động với react + es6

    Trước tiên mình muốn nói qua về con trỏ this trong javascript. Trong các ngôn ngữ hướng đối tượng thông thường thì con trỏ this luôn để chỉ tới class chứa nó tuy nhiên trong javascript trở nên phức tạp hơn rất nhiều khi mà có thêm các khái niệm scoped function, callbacks, closured và promises khiến cỏn trỏ ta không thể kiểm soát được this đang tham chiếu ở đâu.

    Quay lại với chủ đề, React đã chứng tỏ rằng nó là một thư viện thân thiện và tuyệt vời khi làm việc và việc xử lý từ khóa this cũng không ngoại lệ. React mặc định this sẽ được tham chiếu tới bối cảnh(context) bên trong của mỗi component React, ví dụ:

    this.setState({ someKey: someValue })
    

    Tuy nhiên vấn đề sẽ xảy ra ki bạn viết code như sau:

    this.setState({ dataFetched: false })
    
    fetch('/data').then(function fetchComplete() {
      this.setState({ dataFetched: true })
    })
    

    Bạn đã thấy vấn đề xảy ra không? khi mà dòng đầu chạy sẽ không có lỗi gì xảy ra. Tuy nhiên khi chạy đến dòng setState thứ 2 kết quả lỗi sẽ bị raise. Nguyên nhân là dó con trỏ this không còn tham chiếu tới React component nữa mà tham chiếu vào hàm fetchComplete.

    Trước khi giải quyết vấn đề trên mình xin quay lại một chút kiến thức về javascript. Giả sử ta định nghĩa một đối tượng như sau:

    //Định nghĩa 1 đối tượng với phương thức hiển thị thuộc tính ra màn hình
    var Student = {
        name: 'John',
        printName: function() {
           console.log(this.name);
        }
    };
    
    //Bắt sự kiện click chuột lên button, sẽ thực thi hàm hiển thị tên của Student lên màn hình
    $('button').click(Student.printName);    //kq: undefined
    

    Kết quả hiện thị sẽ là undefined nguyên nhân là do con trỏ this không còn trỏ vào đối tượng Student nữa mà nó trỏ vào đối tượng $(‘button’). Để đoạn code trên chạy đúng như mong muốn thì ta phải đảm bảo context của hàm callback Student.printName phải được tham chiếu tới đối tượng Student. Cụ thể ta sẽ sử dụng hàm bind() để gắn context vào hàm callback đó.

    $('button').click(Student.printName.bind(Student));
    

    Các bạn có thể thử tại codepen:

    Đó cũng là lí do tại sao khi viết code trong es6 mặc dù cú pháp hoàn toàn đúng nhưng không thể chạy như mong muốn được

    class ExampleComponent extends React.Component {
      onClickHander() {
        this.setState({ clicked: true });
      }
    
      render() {
        return <div onClick={this.onClickHander}>Click me</div>;
      }
    

    Bạn có thể kiểm chứng tại đây. Cuối cùng là một số giải pháp cho vấn đề trên

    1. Sử dụng bind khi render
    render() {
        return <div onClick={this.onClickHander}>AAAA</div>;
      }
    
    1. Sử dụng arrow function trong JSX
    render() {
        return (
            <div onClick={() => this.onClickHandler()}>AAAA</div>;
        )
    };
    
    1. Bind ngay khi khởi tạo
    class ExampleComponent extends React.Component {
        constructor() {
            super();
            this.onClickHandler = this.onClickHandler.bind(this);
        }
        ...
    }
    
    1. Sử dụng thư viện cho phép autobinding
    import autobind from 'autobind-decorator';
    
    class ExampleComponent extends React.Component {
        @autobind
        update() {
          ...
        }
    
        ...
    }
    
    1. Sử dụng arrow function làm class method
    import React from 'react';
    
    class ExampleComponent extends React.Component {
        update = () => {
          ...
        }
    
        ...
    }
    

    Nguồn: Viblo



Có vẻ như bạn đã mất kết nối tới LaptrinhX, vui lòng đợi một lúc để chúng tôi thử kết nối lại.