Chúng tôi đã làm những điều tệ hại khi đang phát triển 1 dự án, những điều mà đáng ra không nên xảy đến. Thứ tồi tệ đó chính là việc chúng tôi đã dispatch các lời gọi trong các những lifecycle method của React như componentWillReceivePropscomponentWillUpdate, và componentDidUpdate

Đây là 1 trường hợp cụ thể. Chúng tôi có 2 component khác nhau, truy cập vào 2 state khác nhau:

class Address extends React.Component {
  render() {
    return <input value={this.props.address} onChange={this.props.changeAddress}/>;
  }
}
function mapStateToProps(state) {
  return {
    address: state.address
  }
}
function mapDispatchToProps(dispatch) {
  return {
    changeAddress: (address) => dispatch(changeAddress(address)),
  }
}
connect(mapStateToProps, mapDispatchToProps)(Address);

 

class Zipcode extends React.Component {
  render() {
    return <input value={this.props.zipcode} onChange={this.props.changeZipcode}/>;
  }
}
function mapStateToProps(state) {
  return {
    zipcode: state.zipcode
  }
}
function mapDispatchToProps(dispatch) {
  return {
    changeZipcode: (zipcode) => dispatch(changeZipcode(zipcode)),
  }
}
connect(mapStateToProps, mapDispatchToProps)(Zipcode);

 

Bây giờ giả sử tôi muốn thêm 1 thông báo cho component Address khi người dùng nhập vào địa chỉ không xuất hiện trong zip code mà người dùng cung cấp.Ta sẽ lưu trữ dữ liệu của thông báo này trong state redux. Có rất nhiều cách để làm điều này, tuy nhiên tối thiểu thì cách mà ta nên đưa ra phải kiểm tra địa chỉ khi 1) địa chỉ thay đổi2) zip code thay đổi.

Ứng dụng của chúng tôi sử dụng redux-saga. Giả sử rằng ta cần thực thi 1 số lời gọi network để làm công việc kiểm tra. Do đó ta sẽ sử dụng 1 số async middleware.

Giải pháp thứ nhất

Ta dispatch 1 action khi  changeZipcode hoặc changeAddress được gọi đến. Ví dụ, hàm mapDispatchToProps của component Zipcode sẽ thay đổi thành:

function mapDispatchToProps(dispatch) {
  return {
    changeZipcode: (zipcode) => {
      dispatch(changeZipcode(zipcode));
      dispatch(checkAddress());
    }
  }
}

 

Sau đo chúng tôi có saga chờ action checkAddress để làm công việc kiểm tra địa chỉ dựa trên zipcode và cập nhật vài state, trả về và render lại Address component.

Tôi thực sự không thích giải pháp này chút nào do do cả changeZipcode lẫn changeAddress đều sẽ phải sử dụng cùng 1 logic.

Tham khảo các khóa học lập trình online, onlab, và thực tập lập trình tại TechMaster

Giải pháp thứ 2

Có thể sử dụng luôn saga để chờ các action từ changeZipcode hoặc changeAddress và để nó làm công việc kiểm tra khi vấn đề xảy ra. Bây giờ thì chúng tôi không cần action checkAddress.

Giải pháp không nên sử dụng!

Không may là chính chúng tôi đã sử dụng cách này. Các bạn có thể nhận thấy là zipcode cuối cùng vẫn sẽ không thay đổi. Thay vào đó chúng tôi thêm phương thức lifecycle componentWillReceiveProps trong componen Address để kiểm tra khi nào thì địa chỉ hoặc zipcode thay đổi. Nếu có thay đổi xảy ra, dispatch 1 action để kiểm tra địa chỉ.

class Address extends React.Component {
  componentWillReceiveProps(nextProps) {
    if (this.props.zipcode !== nextProps.zipCode || this.props.address !== nextProps.address) {
      this.props.checkAddress();
    }
  }
  render() {
    return (
      <div>
        <p>{this.props.addressAlert}</p>
        <input value={this.props.address} onChange={this.props.changeAddress}/>;
      </div>
    );
  }
}
function mapStateToProps(state) {
  return {
    address: state.address,
    addressAlert: state.addressAlert,
    zipcode: state.zipcode,
  }
}
function mapDispatchToProps(dispatch) {
  return {
    changeAddress: (address) => dispatch(changeAddress(address)),
    checkAddress: () => dispatch(checkAddress()),
  }
}
connect(mapStateToProps, mapDispatchToProps)(Address);

Khi lần đầu thấy đoạn code này tôi đã hơi lạnh lưng. Tôi cuộn chuột lên xuống và tự hỏi Tại sao nhiều lần. Có gì đó sai sai đã xảy ra. Tại sao Address lại cần quan tâm đến zipcoe? Tại sao?

Có thể bạn sẽ cho rằng phương án này đâu có tệ. Thậm chí còn khá tốt nữa đằng khác. Address liên quan đên zipcode bởi vì nó cần cập nhật 1 thông báo về địa chỉ khi zipcode thay đổi. Tuy nhiên vấn đề là như thế này: component không nên quan tâm (hay biết) đến các thay đổi của state bên ngoài nó. Vấn đề này trở nên rõ ràng nhất khi bạn áp dụng redux vào project của bạn.

Các vấn đề tiềm ẩn

Tôi sẽ trình bày thêm 1 số vấn đề nảy sinh của phương án trên. Giả sử bạn sẽ lấy về 1 state khi có sự thay đổi trong ứng dụng, kéo theo các thành phần khác trong ứng dụng thay đổi theo. Lúc này bạn sẽ cần thêm state đó vào bất cứ component nào cần state đó, hay nói cách khác, bạn sẽ phải viết componentWillReceiveProps để kiểm tra trong từng component đó. Vậy lúc này bạn sẽ làm cách nào để đảm bảo 2 component khác nhau không dispatch cùng 1 action khi mà state chúng nhận được thay đổi?

Hoặc tệ hơn, bạn sẽ gặp phải tình trạng sau:

  • Đầu tiên component A dispatch 1 action.
  • Component B lắng nghe sự thay đổi state - kết quả mà component A dispatch, sau đó thực hiện dispatch lần 2.
  • Component C lắng nghe sự thay đổi state - kết quả mà component B dispatch, sau đó thực hiện dispatch lần 3.
  • Và mọi việc vẫn sẽ tiếp tục...

Ứng dụng của bạn sẽ phải trải qua nhiều bước, điều này dẫn đến việc sẽ rất khó để truy ngược xem action nào là action đầu tiên khởi động chuỗi biến đổi này.

Một điều cuối cùng mà tôi muốn nói: việc dùng các phương thức lifecycle theo cách này không hẳn là 1 điều tệ. Chắc chắn bạn sẽ dùng đến chúng 1 khi component của bạn cần có state của riêng nó. Hoặc là bạn cần truy cập thẳng vào DOM do đang làm việc với 1 thư viện không chính chủ React. Tuy nhiên, chúng không bao giờ nên được sử dụng khi mà chúng có thể tạo ra những action làm thay đổi state trong redux, khi phản hồi từ các sự kiện redux.

Kết luận: hãy viết component đơn giản nhất có thể, tốt nhất thì nó phải là 1 hàm của props.

Bài viết được dịch từ: https://hackernoon.com/evil-things-you-do-with-redux-dispatch-in-updating-lifecycle-methods-ad116de882d4