Làm thế nào để một thành phần được kết nối redux biết khi nào cần kết xuất lại?


88

Tôi có lẽ đang thiếu một cái gì đó rất rõ ràng và muốn tự mình giải thích.

Đây là sự hiểu biết của tôi.
Trong một thành phần phản ứng ngây thơ, chúng tôi có states& props. Cập nhật statevới setStatekết xuất toàn bộ thành phần. propshầu hết chỉ được đọc và cập nhật chúng không có ý nghĩa.

Trong một thành phần phản ứng đăng ký một cửa hàng redux, thông qua một cái gì đó như store.subscribe(render), nó hiển nhiên hiển thị lại mỗi khi cửa hàng được cập nhật.

react-redux có một trình trợ giúp connect()đưa một phần của cây trạng thái (quan tâm đến thành phần) và actionCreators propsvào thành phần, thường thông qua một cái gì đó như

const TodoListComponent = connect(
  mapStateToProps,
  mapDispatchToProps
)(TodoList)

Nhưng với sự hiểu biết rằng a setStatelà điều cần thiết TodoListComponentđể phản ứng với sự thay đổi cây trạng thái redux (kết xuất lại), tôi không thể tìm thấy bất kỳ statehoặc setStatemã liên quan nào trong TodoListtệp thành phần. Nó đọc một cái gì đó như thế này:

const TodoList = ({ todos, onTodoClick }) => (
  <ul>
    {todos.map(todo =>
      <Todo
        key={todo.id}
        {...todo}
        onClick={() => onTodoClick(todo.id)}
      />
    )}
  </ul>
)

Ai đó có thể chỉ cho tôi đúng hướng về những gì tôi đang thiếu không?

PS Tôi đang làm theo ví dụ về danh sách việc cần làm đi kèm với gói redux .

Câu trả lời:


111

Các connectchức năng tạo ra một thành phần wrapper mà đặt mua đến các cửa hàng. Khi một hành động được gửi đi, lệnh gọi lại của thành phần trình bao bọc sẽ được thông báo. Sau đó, nó chạy mapStatehàm của bạn và so sánh nông đối tượng kết quả từ lần này với đối tượng kết quả từ lần trước (vì vậy nếu bạn viết lại một trường redux store với cùng giá trị của nó, nó sẽ không kích hoạt kết xuất lại). Nếu kết quả khác nhau, thì nó sẽ chuyển kết quả đến thành phần "thực" của bạn dưới dạng đạo cụ.

Dan Abramov đã viết một phiên bản đơn giản tuyệt vời của connectat ( connect.js ) minh họa ý tưởng cơ bản, mặc dù nó không cho thấy bất kỳ công việc tối ưu hóa nào. Tôi cũng có liên kết đến một số bài báo về hiệu suất Redux thảo luận về một số ý tưởng liên quan.

cập nhật

React-Redux v6.0.0 đã thực hiện một số thay đổi nội bộ lớn về cách các thành phần được kết nối nhận dữ liệu của chúng từ cửa hàng.

Như một phần của điều đó, tôi đã viết một bài đăng giải thích cách connectAPI và các bộ phận bên trong của nó hoạt động và chúng đã thay đổi như thế nào theo thời gian:

Idiomatic Redux: Lịch sử và triển khai của React-Redux


Cảm ơn! Bạn có phải là người đã giúp tôi hôm trước @ HN - news.ycombinator.com/item?id=12307621 với những liên kết hữu ích không? Rất vui được gặp bạn :)
Joe Lewis

4
Yup, đó là tôi :) Tôi dành cách quá nhiều thời gian tìm kiếm các cuộc thảo luận về Redux trực tuyến - Reddit, HN, SO, trung bình, và những nơi khác. Rất vui được giúp đỡ! (Cũng như FYI, tôi thực sự khuyên bạn nên sử dụng các kênh trò chuyện Reactiflux trên Discord. Rất nhiều người đang ở ngoài đó và trả lời các câu hỏi. Tôi thường trực tuyến vào buổi tối theo giờ EST Hoa Kỳ. Liên kết mời có tại Reatiflux.com .)
markerikson

Giả sử điều này đã trả lời câu hỏi, tâm trí chấp nhận câu trả lời? :)
markerikson

Đó là so sánh bản thân các đối tượng hay thuộc tính của những đối tượng trước / sau? Nếu là cái sau, nó chỉ kiểm tra mức đầu tiên, đó có phải ý bạn là "cạn không?"
Andy

Vâng, một "nông séc bình đẳng" có nghĩa là để so sánh các lĩnh vực mức độ đầu tiên của mỗi đối tượng: prev.a === current.a && prev.b === current.b && ...... Điều đó giả định rằng mọi thay đổi dữ liệu sẽ dẫn đến các tham chiếu mới, có nghĩa là cần phải cập nhật dữ liệu bất biến thay vì đột biến trực tiếp.
markerikson

13

Câu trả lời của tôi là một chút ngoài lĩnh vực bên trái. Nó làm sáng tỏ một vấn đề đã dẫn tôi đến bài đăng này. Trong trường hợp của tôi, có vẻ như ứng dụng không hiển thị lại, mặc dù nó đã nhận được các đạo cụ mới.
Các nhà phát triển React đã có câu trả lời cho câu hỏi thường được hỏi này về điều gì đó có nghĩa là nếu (cửa hàng) bị đột biến, 99% thời gian đó là lý do phản ứng sẽ không hiển thị lại. Tuy nhiên, không có gì về 1% còn lại. Đột biến không phải là trường hợp ở đây.


TLDR;

componentWillReceivePropslà cách statecó thể được đồng bộ hóa với cái mới props.

Cạnh Trường hợp: Khi statecập nhật, sau đó ứng dụng không tái render!


Hóa ra là nếu ứng dụng của bạn chỉ sử dụng stateđể hiển thị các phần tử của nó, propscó thể cập nhật, nhưng statesẽ không, do đó không hiển thị lại.

Tôi đã có stateđiều đó phụ thuộc vào propsnhận được từ redux store. Dữ liệu tôi cần vẫn chưa có trong cửa hàng, vì vậy tôi đã tìm nạp nó từ đó componentDidMount, đúng như vậy. Tôi đã nhận lại đạo cụ, khi trình giảm tốc của tôi cập nhật cửa hàng, vì thành phần của tôi được kết nối qua mapStateToProps. Nhưng trang không hiển thị và trạng thái vẫn chứa đầy các chuỗi trống.

Một ví dụ về điều này là giả sử một người dùng đã tải trang "chỉnh sửa bài đăng" từ một url đã lưu. Bạn có quyền truy cập postIdtừ url, nhưng thông tin storechưa có, vì vậy bạn tìm nạp nó. Các mục trên trang của bạn là các thành phần được kiểm soát - vì vậy tất cả dữ liệu bạn đang hiển thị đều nằm trong đó state.

Sử dụng redux, dữ liệu đã được tìm nạp, cập nhật cửa hàng và thành phần được chỉnh sửa connect, nhưng ứng dụng không phản ánh các thay đổi. Khi xem xét kỹ hơn, propsđã nhận được, nhưng ứng dụng không cập nhật. statekhông cập nhật.

Vâng, propssẽ cập nhật và tuyên truyền, nhưng statesẽ không. Bạn cần nói cụ thể stateđể cập nhật.

Bạn không thể làm điều này trong render()componentDidMountđã hoàn thành chu kỳ của nó.

componentWillReceivePropslà nơi bạn cập nhật statecác thuộc tính phụ thuộc vào một propgiá trị đã thay đổi .

Cách sử dụng ví dụ:

componentWillReceiveProps(nextProps){
  if (this.props.post.category !== nextProps.post.category){
    this.setState({
      title: nextProps.post.title,
      body: nextProps.post.body,
      category: nextProps.post.category,
    })
  }      
}

Tôi phải cảm ơn bài viết này đã khai sáng cho tôi về giải pháp mà hàng chục bài đăng, blog và repo khác không đề cập đến. Bất kỳ ai khác gặp khó khăn trong việc tìm câu trả lời cho vấn đề rõ ràng là mơ hồ này, Đây là:

Các phương pháp vòng đời của thành phần ReactJs - Tìm hiểu sâu

componentWillReceivePropslà nơi bạn sẽ cập nhật stateđể đồng bộ hóa với các propsbản cập nhật.

Khi statecập nhật, sau đó các lĩnh vực tùy thuộc vào state việc phải làm lại render!


3
Từ React 16.3trở đi, bạn nên sử dụng getDerivedStateFromPropsthay thế componentWillReceiveProps. Nhưng trên thực tế, bạn thậm chí không nên làm tất cả những gì là khớp nối ở đây
kyw

nó không hoạt động, bất cứ khi nào trạng thái thay đổi thì componentWillReceiveProps sẽ hoạt động nên nhiều trường hợp tôi thử điều này không hoạt động. Đặc biệt, đa cấp lồng nhau hay phức tạp đạo cụ vì vậy tôi phải thành phần assign với một ID và kích hoạt thay đổi cho nó và cập nhật trạng thái để tái làm cho dữ liệu mới
May'st

0

Câu trả lời này là tóm tắt bài báo của Brian Vaughn có tựa đề Bạn có lẽ không cần trạng thái có nguồn gốc (ngày 07 tháng 6 năm 2018).

Trạng thái bắt nguồn từ đạo cụ là một mô hình phản đối trong tất cả các hình thức của nó. Bao gồm cả việc sử dụng cũ hơn componentWillReceivePropsvà mới hơn getDerivedStateFromProps.

Thay vì lấy trạng thái từ đạo cụ, hãy xem xét các giải pháp sau.

Hai khuyến nghị về phương pháp hay nhất

Khuyến nghị 1. Thành phần được kiểm soát hoàn toàn
function EmailInput(props) {
  return <input onChange={props.onChange} value={props.email} />;
}
Khuyến nghị 2. Thành phần hoàn toàn không được kiểm soát bằng khóa
// parent class
class EmailInput extends Component {
  state = { email: this.props.defaultEmail };

  handleChange = event => {
    this.setState({ email: event.target.value });
  };

  render() {
    return <input onChange={this.handleChange} value={this.state.email} />;
  }
}

// child instance
<EmailInput
  defaultEmail={this.props.user.email}
  key={this.props.user.id}
/>

Hai lựa chọn thay thế nếu, vì bất kỳ lý do gì, các đề xuất không phù hợp với tình huống của bạn.

Giải pháp thay thế 1: Đặt lại thành phần không được kiểm soát với một giá đỡ ID
class EmailInput extends Component {
  state = {
    email: this.props.defaultEmail,
    prevPropsUserID: this.props.userID
  };

  static getDerivedStateFromProps(props, state) {
    // Any time the current user changes,
    // Reset any parts of state that are tied to that user.
    // In this simple example, that's just the email.
    if (props.userID !== state.prevPropsUserID) {
      return {
        prevPropsUserID: props.userID,
        email: props.defaultEmail
      };
    }
    return null;
  }

  // ...
}
Phương pháp thay thế 2: Đặt lại thành phần không được kiểm soát bằng một phương thức phiên bản
class EmailInput extends Component {
  state = {
    email: this.props.defaultEmail
  };

  resetEmailForNewUser(newEmail) {
    this.setState({ email: newEmail });
  }

  // ...
}

-2

như tôi biết điều duy nhất redux làm, khi thay đổi trạng thái của cửa hàng là gọi componentWillRecieveProps nếu thành phần của bạn phụ thuộc vào trạng thái bị đột biến và sau đó bạn nên buộc thành phần của mình cập nhật thành phần của nó như thế này

1-store State change-2-call (componentWillRecieveProps (() => {3-component state change}))

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.