Trạng thái ReactJS so với chống đỡ


121

Điều này có thể đang lấn át ranh giới giữa có thể trả lời và có ý kiến, nhưng tôi đang quay đi quay lại làm thế nào để cấu trúc một thành phần ReactJS khi độ phức tạp tăng lên và có thể sử dụng một số hướng.

Đến từ AngularJS, tôi muốn truyền mô hình của mình vào thành phần dưới dạng thuộc tính và để thành phần sửa đổi mô hình trực tiếp. Hay tôi nên chia mô hình thành các statethuộc tính khác nhau và biên dịch nó lại với nhau khi gửi ngược dòng? Cách ReactJS là gì?

Lấy ví dụ về một trình soạn thảo bài đăng trên blog. Cố gắng sửa đổi trực tiếp mô hình sẽ trông giống như:

var PostEditor = React.createClass({
  updateText: function(e) {
    var text = e.target.value;
    this.props.post.text = text;
    this.forceUpdate();
  },
  render: function() {
    return (
      <input value={this.props.post.text} onChange={this.updateText}/>
      <button onClick={this.props.post.save}/>Save</button>
    );
  }
});

Có vẻ như sai.

Nó có phải là cách React nhiều hơn để tạo thuộc tính textmô hình của chúng tôi statevà biên dịch nó trở lại mô hình trước khi lưu như:

var PostEditor = React.createClass({
  getInitialState: function() {
    return {
      text: ""
    };
  },
  componentWillMount: function() {
    this.setState({
      text: this.props.post.text
    });
  },
  updateText: function(e) {
    this.setState({
      text: e.target.value
    });
  },
  savePost: function() {
    this.props.post.text = this.state.text;
    this.props.post.save();
  },
  render: function() {
    return (
      <input value={this.state.text} onChange={this.updateText}/>
      <button onClick={this.savePost}/>Save</button>
    );
  }
});

Điều này không yêu cầu gọi đến this.forceUpdate(), nhưng khi mô hình phát triển, (một bài đăng có thể có tác giả, chủ đề, thẻ, nhận xét, xếp hạng, v.v.) thì thành phần bắt đầu thực sự phức tạp.

Phương pháp đầu tiên với ReactLinkphải là cách tốt nhất không?

Câu trả lời:


64

Cách tiếp cận thứ hai của bạn giống nó hơn. React không quan tâm đến các mô hình quá nhiều vì nó quan tâm đến các giá trị và cách chúng chạy qua ứng dụng của bạn. Lý tưởng nhất là mô hình bài đăng của bạn sẽ được lưu trữ trong một thành phần duy nhất ở gốc. Sau đó, bạn tạo các thành phần con mà mỗi thành phần sử dụng các bộ phận của mô hình.

Bạn có thể chuyển các lệnh gọi lại đến các phần tử con cần sửa đổi dữ liệu của bạn và gọi chúng từ thành phần con.

Sửa đổi this.props hoặc this.state trực tiếp không phải là một ý tưởng hay, vì React sẽ không thể tiếp nhận các thay đổi. Đó là bởi vì React thực hiện một so sánh nông cạn của bài đăng của bạn để xác định xem nó có thay đổi hay không.

Tôi đã làm jsfiddle này để chỉ ra cách dữ liệu có thể chảy từ thành phần bên ngoài vào bên trong.

Các handleClickphương pháp hiển thị 3 cách để (im) đúng trạng thái cập nhật:

var Outer = React.createClass({

  getInitialState: function() {
    return {data: {value: 'at first, it works'}};
  },

  handleClick: function () {

    // 1. This doesn't work, render is not triggered.
    // Never set state directly because the updated values
    // can still be read, which can lead to unexpected behavior.

    this.state.data.value = 'but React will never know!';

    // 2. This works, because we use setState

    var newData = {value: 'it works 2'};
    this.setState({data: newData});

    // 3. Alternatively you can use React's immutability helpers
    // to update more complex models.
    // Read more: http://facebook.github.io/react/docs/update.html

    var newState = React.addons.update(this.state, {
      data: {value: {$set: 'it works'}}
    });
    this.setState(newState);
 },

  render: function() {
      return <Inner data={this.state.data} handleClick={this.handleClick} />;
  }
});

Nhưng chúng ta phải làm gì nếu chúng ta có một mô hình mờ, với các chức năng thao tác trạng thái riêng của nó? Ví dụ: giả sử rằng thay vì một texttrường, chúng ta có một setText phương thức thực hiện xác thực và một số nội dung khác. Tôi có thể thấy phương thức (2) hoạt động nếu setTextlà thuần túy và trả về một phiên bản hoàn toàn mới của mô hình. Tuy nhiên, nếu setTextchỉ cập nhật trạng thái bên trong, chúng ta vẫn cần gọi forceUpdate, phải không?
ômomg

1
Có, bạn có thể gọi forceUpdate, nhưng tại thời điểm đó, bạn đang "rò rỉ" khỏi React. Tốt hơn là nên chuyển một lệnh setState()gọi lại đến mô hình không rõ ràng để tránh phải kích hoạt hiển thị lại theo cách thủ công.
jxg

Tôi vẫn không chắc mình hoàn toàn hiểu. Vì vậy, bất kỳ thành phần nào nhằm mục đích sửa đổi dữ liệu cần phải thực hiện một bản sao sâu của các đạo cụ được chuyển vào? Sau đó, sửa đổi và gửi bản sao đó ngược dòng để không sửa đổi dữ liệu gốc? Cuối cùng thì thay đổi sẽ đến tận gốc, nơi nó được xử lý và toàn bộ ứng dụng được hiển thị? Có đúng không?
nicholas

97

Cập nhật năm 2016: React được thay đổi và giải thích "props vs state" trở nên rất đơn giản. Nếu một thành phần cần thay đổi dữ liệu - hãy đặt nó ở trạng thái, nếu không thì trong đạo cụ. Vì đạo cụ ở chế độ chỉ đọc .

Sự khác biệt chính xác giữa đạo cụ và trạng thái là gì?

Bạn có thể tìm thấy lời giải thích hay ở đây (phiên bản đầy đủ)

Thay đổi đạo cụ và trạng thái


1
thực sự setProps () có thể thay đổi đạo cụ bên trong một thành phần và kích hoạt một tái làm
WaiKit Kung

2
setPropskhông được dùng nữa và không nên được sử dụng. Thay thế là kết xuất thành phần và để React xử lý các điểm khác biệt.
jdmichal

Và nếu bạn đang tìm kiếm một video giải thích: youtube.com/watch?v=qh3dYM6Keuw
jaisonDavis

35

Từ tài liệu React

props là bất biến: chúng được chuyển từ cha mẹ và được "sở hữu" bởi cha mẹ. Để triển khai các tương tác, chúng tôi giới thiệu trạng thái có thể thay đổi cho thành phần. this.state là riêng tư đối với thành phần và có thể được thay đổi bằng cách gọi this.setState (). Khi trạng thái được cập nhật, thành phần tự hiển thị lại.

Từ TrySpace : khi các đạo cụ (hoặc trạng thái) được cập nhật (thông qua setProps / setState hoặc cha), thành phần cũng hiển thị lại.


16

Bài đọc từ Thinking in React :

Hãy đi qua từng cái và tìm ra cái nào là trạng thái. Chỉ cần đặt ba câu hỏi về mỗi phần dữ liệu:

  1. Nó có được truyền từ cha mẹ thông qua đạo cụ không? Nếu vậy, nó có thể không phải là trạng thái.
  2. Nó có thay đổi theo thời gian không? Nếu không, nó có thể không phải là trạng thái.

  3. Bạn có thể tính toán nó dựa trên bất kỳ trạng thái hoặc đạo cụ nào khác trong thành phần của bạn không? Nếu vậy, nó không phải là trạng thái.


13

Tôi không chắc liệu mình có đang trả lời câu hỏi của bạn hay không, nhưng tôi thấy rằng, đặc biệt là trong một ứng dụng lớn / đang phát triển, mẫu Container / Component hoạt động cực kỳ hiệu quả.

Về cơ bản, bạn có hai thành phần React:

  • thành phần hiển thị "thuần túy", xử lý kiểu và tương tác DOM;
  • một thành phần vùng chứa, xử lý việc truy cập / lưu dữ liệu bên ngoài, quản lý trạng thái và kết xuất thành phần hiển thị.

Thí dụ

NB Ví dụ này có lẽ quá đơn giản để minh họa lợi ích của mô hình này, vì nó khá dài dòng đối với trường hợp đơn giản như vậy.

/**
 * Container Component
 *
 *  - Manages component state
 *  - Does plumbing of data fetching/saving
 */

var PostEditorContainer = React.createClass({
  getInitialState: function() {
    return {
      text: ""
    };
  },

  componentWillMount: function() {
    this.setState({
      text: getPostText()
    });
  },

  updateText: function(text) {
    this.setState({
      text: text
    });
  },

  savePost: function() {
    savePostText(this.state.text);
  },

  render: function() {
    return (
      <PostEditor
        text={this.state.text}
        onChange={this.updateText.bind(this)}
        onSave={this.savePost.bind(this)}
      />
    );
  }
});


/**
 * Pure Display Component
 *
 *  - Calculates styling based on passed properties
 *  - Often just a render method
 *  - Uses methods passed in from container to announce changes
 */

var PostEditor = React.createClass({
  render: function() {
    return (
      <div>
        <input type="text" value={this.props.text} onChange={this.props.onChange} />
        <button type="button" onClick={this.props.onSave} />
      </div>
    );
  }
});

Những lợi ích

Bằng cách giữ logic hiển thị và dữ liệu / quản lý trạng thái riêng biệt, bạn có một thành phần hiển thị có thể sử dụng lại:

  • có thể dễ dàng lặp lại với các bộ đạo cụ khác nhau bằng cách sử dụng thứ gì đó như react-component-sân chơi
  • có thể được bọc bằng một vùng chứa khác cho các hành vi khác nhau (hoặc kết hợp với các thành phần khác để xây dựng các phần lớn hơn trong ứng dụng của bạn

Bạn cũng có một thành phần vùng chứa xử lý tất cả các giao tiếp bên ngoài. Điều này sẽ giúp bạn linh hoạt hơn về cách truy cập dữ liệu của mình nếu bạn thực hiện bất kỳ thay đổi nghiêm trọng nào sau này *.

Mô hình này cũng làm cho việc viết và thực hiện các bài kiểm tra đơn vị trở nên đơn giản hơn rất nhiều.

Sau khi lặp lại một ứng dụng React lớn một vài lần, tôi nhận thấy rằng mẫu này giúp mọi thứ tương đối dễ dàng, đặc biệt khi bạn có các thành phần lớn hơn với các kiểu được tính toán hoặc các tương tác DOM phức tạp.

* Đọc về mô hình dòng chảy và xem qua Marty.js , phần lớn truyền cảm hứng cho câu trả lời này (và tôi đã sử dụng rất nhiều gần đây) Redux (và react-redux ), thực hiện mô hình này cực kỳ tốt.

Lưu ý cho những người đọc sách này vào năm 2018 trở lên:

React đã phát triển khá nhiều kể từ khi câu trả lời này được viết, đặc biệt là với sự ra đời của Hooks . Tuy nhiên, logic quản lý trạng thái cơ bản từ ví dụ này vẫn giữ nguyên và quan trọng hơn, những lợi ích mà bạn nhận được từ việc giữ trạng thái và logic trình bày riêng biệt vẫn áp dụng theo các cách tương tự.


0

Tôi nghĩ rằng bạn đang sử dụng một mô hình chống mà Facebook đã giải thích tại liên kết này

Đây là thứ bạn đang tìm thấy:

React.createClass({
  getInitialState: function() {
    return { value: { foo: 'bar' } };
  },

  onClick: function() {
    var value = this.state.value;
    value.foo += 'bar'; // ANTI-PATTERN!
    this.setState({ value: value });
  },

  render: function() {
    return (
      <div>
        <InnerComponent value={this.state.value} />
        <a onClick={this.onClick}>Click me</a>
      </div>
    );
  }
});

Lần đầu tiên thành phần bên trong được hiển thị, nó sẽ có {foo: 'bar'} làm giá trị hỗ trợ. Nếu người dùng nhấp vào neo, trạng thái của thành phần chính sẽ được cập nhật thành {value: {foo: 'barbar'}}, kích hoạt quá trình kết xuất của thành phần bên trong, thành phần này sẽ nhận được {foo: 'barbar'} là giá trị mới cho chỗ dựa.

Vấn đề là vì thành phần cha và thành phần bên trong chia sẻ một tham chiếu đến cùng một đối tượng, khi đối tượng bị biến đổi trên dòng 2 của hàm onClick, phần hỗ trợ mà thành phần bên trong có sẽ thay đổi. Vì vậy, khi quá trình kết xuất bắt đầu và shouldComponentUpdate được gọi, this.props.value.foo sẽ bằng nextProps.value.foo, vì trên thực tế, this.props.value tham chiếu cùng một đối tượng như nextProps.value.

Do đó, vì chúng ta sẽ bỏ lỡ sự thay đổi về giá đỡ và đoản mạch trong quá trình kết xuất, giao diện người dùng sẽ không được cập nhật từ 'bar' thành 'barbar'


Bạn cũng có thể vui lòng đăng Innercomponentsmã?
Abdullah Khan
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.