React Context'e render işlevi dışında erişin


94

Redux yerine yeni React Context API kullanarak yeni bir uygulama geliştiriyorum ve daha önce, Reduxörneğin bir kullanıcı listesi almam gerektiğinde, sadece componentDidMounteylemimi çağırıyorum , ancak şimdi React Context ile eylemlerim içeride yaşıyor Benim render fonksiyonumun içinde bulunan Tüketicim, yani render fonksiyonum her çağrıldığında, kullanıcı listemi almak için eylemimi çağıracak ve bu iyi değil çünkü çok fazla gereksiz istek yapıyor olacağım.

Öyleyse, componentDidMountrender'da çağırmak yerine, eylemimi sadece bir kez çağırabilirim ?

Örnek vermek için şu koda bakın:

Diyelim ki hepsini Providerstek bir bileşene sardım, şöyle:

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>
    )
  }
}

Sonra bu Sağlayıcı bileşenini tüm uygulamamı şu şekilde sarmaladım:

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>
    )
  }
}

Şimdi, örneğin kullanıcılarımın görüşüne göre, şöyle bir şey olacak:

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>
    )
  }
}

İstediğim şu:

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>
    )
  }
}

Ancak elbette yukarıdaki örnek çalışmıyor çünkü getUsersKullanıcılarımın görünümünde yaşamıyor. Bu mümkünse, bunu yapmanın doğru yolu nedir?

Yanıtlar:


145

DÜZENLEME: tepki-kanca tanıtılmasıyla v16.8.0 , sen yararlanarak fonksiyonel bileşenleri bağlamda kullanabilirsiniz useContextkanca

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

DÜZENLEME: 16.6.0 sürümünden itibaren. Sen kullanarak yaşam döngüsü yöntemi bağlamında yararlanabilirler this.contextgibi

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

16.6.0 sürümünden önce, bunu aşağıdaki şekilde yapabilirdiniz

Yaşam tarzı yönteminizde Bağlamı kullanmak için bileşeninizi şöyle yazarsınız:

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>
)

Genellikle Uygulamanızda bir bağlam tutarsınız ve yukarıdaki girişi yeniden kullanmak için bir HOC'de paketlemek mantıklıdır. Gibi yazabilirsin

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>
    );
  };
};

ve sonra onu şu şekilde kullanabilirsin

export default withUserContext(User);

Bunu yapmanın bir yolu bu! Ancak bileşenlerimi başka şeylerle sarıyorum, böylece kod bu şekilde gittikçe daha karmaşık hale gelecektir. Dolayısıyla, bağlamlı kitaplığı ve bunun dekoratörlerini kullanmak kodu çok daha basit hale getirir. Ama cevap için teşekkürler!
Gustavo Mendonça

Benim için çalışmıyor, bağlamlı github kullanıyorum
PassionateDeveloper

1
Shubham Khatri cevabında haklı, bunun çalışmasını engelleyen küçük bir yazım hatası var. Küme parantezleri örtük dönüşü engelliyor. Parantez ile değiştirin.
VDubs

1
this.contextAynı bileşen içinde birden çok bağlamla nasıl kullanılacağını açıklayabilir misiniz ? Diyelim ki UserContext ve PostContext'e erişmesi gereken bir bileşenim var, bunu nasıl halledebilirim? Görebildiğim şey, her ikisini de karıştıran bir bağlam oluşturmak, ancak bunun için tek veya daha iyi çözüm bu mu?
Gustavo Mendonça

1
@ GustavoMendonça this.context API uygulamasını kullanarak yalnızca tek bir bağlama abone olabilirsiniz. Birden fazla okumanız gerekiyorsa, render prop modelini izlemelisiniz
Shubham Khatri

3

Tamam, bunu bir sınırlama ile yapmanın bir yolunu buldum. with-contextKütüphane ile tüm tüketici verilerimi bileşen donanımıma eklemeyi başardım.

Ancak, aynı bileşene birden fazla tüketici eklemek karmaşıktır, bu kitaplıkla karma tüketiciler yaratmanız gerekir, bu da kodu zarifleştirmez ve üretken değildir.

Bu kitaplığa bağlantı: https://github.com/SunHuawei/with-context

DÜZENLEME: Aslında with-context, basit API'yi kullanan ve bağlamınızın her biri için bir dekoratör sağlayan çoklu bağlamlı API'yi kullanmanız gerekmez ve bileşeninizde birden fazla tüketici kullanmak istiyorsanız, yalnızca sınıfınızın üzerinde istediğiniz kadar dekoratör ilan edin!


1

Benim açımdan .bind(this)etkinliğe eklemek yeterliydi . Bileşenim böyle görünüyor.

// 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
bu kod çok daha basit görünüyor, ancak gerçekte değerlere erişmiyor veya RootStore örneğiyle hiç etkileşime girmiyor. iki dosyaya bölünmüş bir örnek ve reaktif kullanıcı arayüzü güncellemeleri gösterebilir misiniz?
dcsan

-8

Alt öğede bir destek olarak erişim elde etmek için daha yüksek üst bileşende bağlamı aktarmanız gerekir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.