Truy cập React Context bên ngoài chức năng kết xuất


93

Tôi đang phát triển một ứng dụng mới bằng API ngữ cảnh React mới thay vì Redux và trước đây Redux, khi tôi cần lấy danh sách người dùng, chẳng hạn, tôi chỉ cần gọi vàocomponentDidMount hành động của mình, nhưng bây giờ với React Context, các hành động của tôi hoạt động bên trong Người tiêu dùng của tôi bên trong hàm kết xuất của tôi, có nghĩa là mỗi khi hàm kết xuất của tôi được gọi, nó sẽ gọi hành động của tôi để lấy danh sách người dùng của tôi và điều đó không tốt vì tôi sẽ thực hiện rất nhiều yêu cầu không cần thiết.

Vì vậy, làm cách nào để tôi có thể gọi chỉ một lần hành động của mình, như trong componentDidMount thay vì gọi trong hiển thị?

Để làm ví dụ, hãy xem mã này:

Giả sử rằng tôi đang gói tất cả Providerstrong một thành phần, như thế này:

import React from 'react';

import UserProvider from './UserProvider';
import PostProvider from './PostProvider';

export default class Provider extends React.Component {
  render(){
    return(
      <UserProvider>
        <PostProvider>
          {this.props.children}
        </PostProvider>
      </UserProvider>
    )
  }
}

Sau đó, tôi đặt thành phần Nhà cung cấp này gói tất cả ứng dụng của tôi, như thế này:

import React from 'react';
import Provider from './providers/Provider';
import { Router } from './Router';

export default class App extends React.Component {
  render() {
    const Component = Router();
    return(
      <Provider>
        <Component />
      </Provider>
    )
  }
}

Bây giờ, ở chế độ xem người dùng của tôi chẳng hạn, nó sẽ giống như thế này:

import React from 'react';
import UserContext from '../contexts/UserContext';

export default class Users extends React.Component {
  render(){
    return(
      <UserContext.Consumer>
        {({getUsers, users}) => {
          getUsers();
          return(
            <h1>Users</h1>
            <ul>
              {users.map(user) => (
                <li>{user.name}</li>
              )}
            </ul>
          )
        }}
      </UserContext.Consumer>
    )
  }
}

Điều tôi muốn là:

import React from 'react';
import UserContext from '../contexts/UserContext';

export default class Users extends React.Component {
  componentDidMount(){
    this.props.getUsers();
  }

  render(){
    return(
      <UserContext.Consumer>
        {({users}) => {
          getUsers();
          return(
            <h1>Users</h1>
            <ul>
              {users.map(user) => (
                <li>{user.name}</li>
              )}
            </ul>
          )
        }}
      </UserContext.Consumer>
    )
  }
}

Nhưng tất nhiên, ví dụ trên không hoạt động bởi vì các getUsersđạo cụ không xuất hiện trong chế độ xem Người dùng của tôi. Cách đúng để làm điều đó là gì nếu điều này hoàn toàn có thể?

Câu trả lời:


144

CHỈNH SỬA: Với sự ra đời của react -hook trong v16.8.0 , bạn có thể sử dụng ngữ cảnh trong các thành phần chức năng bằng cách sử dụng useContexthook

const Users = () => {
    const contextValue = useContext(UserContext);
    // rest logic here
}

CHỈNH SỬA: Từ phiên bản 16.6.0 trở đi. Bạn có thể sử dụng ngữ cảnh trong phương pháp vòng đời bằng cách sử dụng this.contextnhư

class Users extends React.Component {
  componentDidMount() {
    let value = this.context;
    /* perform a side-effect at mount using the value of UserContext */
  }
  componentDidUpdate() {
    let value = this.context;
    /* ... */
  }
  componentWillUnmount() {
    let value = this.context;
    /* ... */
  }
  render() {
    let value = this.context;
    /* render something based on the value of UserContext */
  }
}
Users.contextType = UserContext; // This part is important to access context values

Trước phiên bản 16.6.0, bạn có thể thực hiện theo cách sau

Để sử dụng Ngữ cảnh trong phương thức phong cách sống của bạn, bạn sẽ viết thành phần của mình như

class Users extends React.Component {
  componentDidMount(){
    this.props.getUsers();
  }

  render(){
    const { users } = this.props;
    return(

            <h1>Users</h1>
            <ul>
              {users.map(user) => (
                <li>{user.name}</li>
              )}
            </ul>
    )
  }
}
export default props => ( <UserContext.Consumer>
        {({users, getUsers}) => {
           return <Users {...props} users={users} getUsers={getUsers} />
        }}
      </UserContext.Consumer>
)

Nói chung, bạn sẽ duy trì một ngữ cảnh trong Ứng dụng của mình và việc đóng gói thông tin đăng nhập ở trên trong HOC để sử dụng lại là điều hợp lý. Bạn có thể viết nó như

import UserContext from 'path/to/UserContext';

const withUserContext = Component => {
  return props => {
    return (
      <UserContext.Consumer>
        {({users, getUsers}) => {
          return <Component {...props} users={users} getUsers={getUsers} />;
        }}
      </UserContext.Consumer>
    );
  };
};

và sau đó bạn có thể sử dụng nó như

export default withUserContext(User);

Đó là một cách để làm điều đó! Nhưng tôi đang gói các thành phần của mình bằng những thứ khác, vì vậy mã sẽ ngày càng phức tạp hơn khi làm theo cách này. Vì vậy, việc sử dụng thư viện với ngữ cảnh và trình trang trí của nó làm cho mã đơn giản hơn nhiều. Nhưng cảm ơn vì câu trả lời!
Gustavo Mendonça

Nó không hoạt động đối với tôi, tôi sử dụng github có ngữ cảnh
Đam

1
Shubham Khatri đã chính xác trong câu trả lời của mình, chỉ có một lỗi đánh máy nhỏ ngăn điều này chạy. Các dấu ngoặc nhọn đang ngăn cản việc trả về ngầm định. Chỉ cần thay thế chúng bằng dấu ngoặc đơn.
VDubs

1
Bạn có thể giải thích cách sử dụng this.contextvới nhiều ngữ cảnh bên trong cùng một thành phần không? Giả sử tôi có một thành phần cần truy cập UserContext và PostContext, làm thế nào để xử lý điều đó? Những gì tôi có thể thấy là tạo bối cảnh trộn lẫn cả hai, nhưng đó là giải pháp duy nhất hoặc tốt hơn cho điều này?
Gustavo Mendonça

1
@ GustavoMendonça Bạn chỉ có thể đăng ký một ngữ cảnh duy nhất bằng cách sử dụng triển khai API this.context. Nếu bạn cần đọc nhiều hơn một, bạn cần làm theo mô hình hỗ trợ kết xuất
Shubham Khatri

3

Ok, tôi đã tìm thấy một cách để làm điều này với một giới hạn. Với with-contextthư viện, tôi đã quản lý để chèn tất cả dữ liệu người tiêu dùng vào các đạo cụ thành phần của mình.

Tuy nhiên, để chèn nhiều người tiêu dùng vào cùng một thành phần thì rất phức tạp, bạn phải tạo nhiều người tiêu dùng hỗn hợp với thư viện này, điều này làm cho mã không đẹp và không hiệu quả.

Liên kết đến thư viện này: https://github.com/SunHuawei/with-context

CHỈNH SỬA: Trên thực tế, bạn không cần sử dụng api đa ngữ cảnh được with-contextcung cấp, trên thực tế, bạn có thể sử dụng api đơn giản và tạo trình trang trí cho từng ngữ cảnh của mình và nếu bạn muốn sử dụng nhiều hơn một người tiêu dùng trong thành phần của mình, chỉ cần khai báo trên lớp của bạn bao nhiêu trình trang trí tùy thích!


1

Về phần tôi, nó đã đủ để thêm .bind(this)vào sự kiện. Đây là cách Thành phần của tôi trông như thế nào.

// Stores File
class RootStore {
   //...States, etc
}
const myRootContext = React.createContext(new RootStore())
export default myRootContext;


// In Component
class MyComp extends Component {
  static contextType = myRootContext;

  doSomething() {
   console.log()
  }

  render() {
    return <button onClick={this.doSomething.bind(this)}></button>
  }
}

1
mã này trông đơn giản hơn rất nhiều, nhưng nó không thực sự truy cập các giá trị hoặc tương tác với phiên bản RootStore. bạn có thể hiển thị một ví dụ tách thành hai tệp và cập nhật giao diện người dùng phản ứng không?
dcsan

-7

Bạn phải chuyển ngữ cảnh trong thành phần cha cao hơn để có quyền truy cập như một đạo cụ ở con.

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.