React Context'i bir alt bileşenin içinden nasıl güncelleyebilirim?


100

Aşağıdaki gibi bağlamda dil ayarlarına sahibim

class LanguageProvider extends Component {
  static childContextTypes = {
    langConfig: PropTypes.object,
  };

  getChildContext() {
    return { langConfig: 'en' };
  }

  render() {
    return this.props.children;
  }
}

export default LanguageProvider;

Başvuru kodum aşağıdaki gibi olacak

<LanguageProvider>
  <App>
    <MyPage />
  </App>
</LanguageProvider>

Sayfamda dili değiştirmek için bir bileşen var

<MyPage>
  <LanguageSwitcher/>
</MyPage>

LanguageSwitcher bu MyPagedurumda dili aşağıdaki gibi 'jp' olarak değiştirmek için bağlamı güncellemeniz gerekir

class LanguageSwitcher extends Component {
  static contextTypes = {
    langConfig: PropTypes.object,
  };

  updateLanguage() {
    //Here I need to update the langConfig to 'jp' 
  }

  render() {
    return <button onClick={this.updateLanguage}>Change Language</button>;
  }
}

export default LanguageSwitcher;

İçeriği LanguageSwitcher bileşeninin içinden nasıl güncelleyebilirim?


Bunu okudun mu facebook.github.io/react/docs/context.html#updating-context Belki de bu durum bağlam için değil durum için daha uygun bir şeydir
azium

@azium Evet .. Bu dokümanda içerik, bileşenin kendisinden güncellenir veya dokümanda içerik sağlayıcıya props olarak aktarılan içeriği içeren bir blog bağlantısı eklendi - onu alt bileşenden güncellemem gerekiyor
mshameer

Hayır, belge güncellemeniz gerekiyorsa bağlamı kullanmamayı söylemiyor. Kesin olmak gerekirse "bunu yapma". Tekrar ediyorum, durumu değil bağlamı kullan
azium

2
@LondonRob ne tür kanonik cevaplar arıyorsunuz? IMO belgelerin içeriği bana gayet iyi görünüyor. Bağlamı bir çocukta ayarlamak istiyorsanız, sağlayıcının bileşeninde bir belirleyici oluşturun ve bunu bir çocuk tüketiciye aktarın. Daha sonra çocuk tüketicideki bu belirleyiciyi çağırın ve onu çocuktaki hangi veriye sahipse ona ayarlayın. Yine de React'in verileri kaldırma fikrini sürdürüyor.
Andrew Li

2
@azium, tüm bu yıllar sonra bu yorumu okuyan diğerlerine bir uyarı. Bağlamın bir alt bileşenden güncellenmesi artık destekleniyor ve oldukça basit: hyp.is/FiP3mG6fEeqJiOfWzfKpgw/reactjs.org/docs/context.html
lustig

Yanıtlar:


228

Kancaları kullanma

Hook'lar 16.8.0'da tanıtıldı, bu nedenle aşağıdaki kod en az 16.8.0 sürümünü gerektirir (sınıf bileşenleri örneği için aşağı kaydırın). CodeSandbox Demosu

1. Dinamik bağlam için üst durumu ayarlama

İlk olarak, tüketicilere aktarılabilecek dinamik bir bağlama sahip olmak için ebeveynin durumunu kullanacağım. Bu, benim tek bir doğruluk kaynağımın ortaya çıkmasını sağlar. Örneğin, ebeveyn uygulamam şöyle görünecek:

const App = () => {
  const [language, setLanguage] = useState("en");
  const value = { language, setLanguage };

  return (
    ...
  );
};

languageHalde depolanır. Her ikisini de languageve ayarlayıcı işlevini setLanguagedaha sonra bağlam aracılığıyla geçireceğiz .

2. Bir bağlam oluşturmak

Sonra, bunun gibi bir dil bağlamı oluşturdum:

// set the defaults
const LanguageContext = React.createContext({
  language: "en",
  setLanguage: () => {}
});

Burada language('en') için varsayılanları setLanguageve içerik sağlayıcı tarafından tüketicilere gönderilecek bir işlevi ayarlıyorum . Bunlar yalnızca varsayılanlardır ve üst öğede sağlayıcı bileşenini kullanırken değerlerini sağlayacağım App.

Not: LanguageContextaynı kalır

3. Bir bağlam tüketicisi oluşturma

Dil değiştiricinin dili ayarlaması için, bağlam aracılığıyla dil belirleyici işlevine erişebilmesi gerekir. Şunun gibi görünebilir:

const LanguageSwitcher = () => {
  const { language, setLanguage } = useContext(LanguageContext);
  return (
    <button onClick={() => setLanguage("jp")}>
      Switch Language (Current: {language})
    </button>
  );
};

Burada dili 'jp' olarak ayarlıyorum ama bunun için dilleri ayarlamak için kendi mantığınız olabilir.

4. Tüketiciyi bir sağlayıcıya sarmak

Şimdi dil değiştirici bileşenimi a'da oluşturacağım LanguageContext.Providerve bağlam aracılığıyla gönderilmesi gereken değerleri daha derine aktaracağım. İşte ebeveynim Appşöyle görünüyor:

const App = () => {
  const [language, setLanguage] = useState("en");
  const value = { language, setLanguage };

  return (
    <LanguageContext.Provider value={value}>
      <h2>Current Language: {language}</h2>
      <p>Click button to change to jp</p>
      <div>
        {/* Can be nested */}
        <LanguageSwitcher />
      </div>
    </LanguageContext.Provider>
  );
};

Artık, dil değiştirici her tıklandığında bağlamı dinamik olarak günceller.

CodeSandbox Demosu

Sınıf bileşenlerini kullanma

En son bağlam API'si , dinamik bir bağlama sahip olmanın harika bir yolunu sağlayan React 16.3'te tanıtıldı. Aşağıdaki kod, minimum 16.3.0 sürümünü gerektirir. CodeSandbox Demosu

1. Dinamik bağlam için üst durumu ayarlama

İlk olarak, tüketicilere aktarılabilecek dinamik bir bağlama sahip olmak için ebeveynin durumunu kullanacağım. Bu, benim tek bir doğruluk kaynağımın ortaya çıkmasını sağlar. Örneğin, ebeveyn uygulamam şöyle görünecek:

class App extends Component {
  setLanguage = language => {
    this.setState({ language });
  };

  state = {
    language: "en",
    setLanguage: this.setLanguage
  };

  ...
}

, Durum languageağacının dışında tutabileceğiniz bir dil belirleyici yöntemi ile birlikte durumda depolanır.

2. Bir bağlam oluşturmak

Sonra, bunun gibi bir dil bağlamı oluşturdum:

// set the defaults
const LanguageContext = React.createContext({
  language: "en",
  setLanguage: () => {}
});

Burada language('en') için varsayılanları setLanguageve içerik sağlayıcı tarafından tüketicilere gönderilecek bir işlevi ayarlıyorum . Bunlar yalnızca varsayılanlardır ve üst öğede sağlayıcı bileşenini kullanırken değerlerini sağlayacağım App.

3. Bir bağlam tüketicisi oluşturma

Dil değiştiricinin dili ayarlaması için, bağlam aracılığıyla dil belirleyici işlevine erişebilmesi gerekir. Şunun gibi görünebilir:

class LanguageSwitcher extends Component {
  render() {
    return (
      <LanguageContext.Consumer>
        {({ language, setLanguage }) => (
          <button onClick={() => setLanguage("jp")}>
            Switch Language (Current: {language})
          </button>
        )}
      </LanguageContext.Consumer>
    );
  }
}

Burada dili 'jp' olarak ayarlıyorum ama bunun için dilleri ayarlamak için kendi mantığınız olabilir.

4. Tüketiciyi bir sağlayıcıya sarmak

Şimdi dil değiştirici bileşenimi a'da oluşturacağım LanguageContext.Providerve bağlam aracılığıyla gönderilmesi gereken değerleri daha derine aktaracağım. İşte ebeveynim Appşöyle görünüyor:

class App extends Component {
  setLanguage = language => {
    this.setState({ language });
  };

  state = {
    language: "en",
    setLanguage: this.setLanguage
  };

  render() {
    return (
      <LanguageContext.Provider value={this.state}>
        <h2>Current Language: {this.state.language}</h2>
        <p>Click button to change to jp</p>
        <div>
          {/* Can be nested */}
          <LanguageSwitcher />
        </div>
      </LanguageContext.Provider>
    );
  }
}

Artık, dil değiştirici her tıklandığında bağlamı dinamik olarak günceller.

CodeSandbox Demosu


bağlamı başlattığınız varsayılan değerlerin amacı nedir? Bu varsayılanlar her zaman tarafından geçersiz kılınmıyor Providermu?
ecoe

@ecoe doğru, ancak sağlayıcı hayır'ı geçerse value, varsayılanlar tüketici tarafından kullanılacaktır.
Divyanshu Maithani

1
Neden bağlamlar tek bir değeri ayarlamak / almak için sınırlandırılıyor ... Bu çok verimsiz görünüyor. Daha iyi bir örnek, bir nesne olarak varsayılanı olan bir bağlamı vurgulamak ve nesneyi buna göre güncellemek olacaktır.
AlxVallejo

Bahsettiğiniz şey için bağlamları kullanmak mümkündür. Cevaptaki örnek soruya göre geri dönüştü. Bu nedenle, kısalık için yalnızca tek bir değer içerir.
Divyanshu Maithani

1
SetLanguage parametresi yoksa Typescript benim durumumda şikayet ediyor. setLanguage: (language: string) => {}benim için çalışıyor.
alex351

49

Maithani'nin yukarıdaki cevabı harika bir çözümdür, ancak bu, kanca kullanılmayan bir sınıf bileşenidir. React tarafından işlevsel bileşenlerin ve kancaların kullanılması önerildiği için, bunu useContext ve useState kancaları ile uygulayacağım. Bir alt bileşenin içinden bağlamı nasıl güncelleyebileceğiniz aşağıda açıklanmıştır.

LanguageContextMangement.js

import React, { useState } from 'react'

export const LanguageContext = React.createContext({
  language: "en",
  setLanguage: () => {}
})

export const LanguageContextProvider = (props) => {

  const setLanguage = (language) => {
    setState({...state, language: language})
  }

  const initState = {
    language: "en",
    setLanguage: setLanguage
  } 

  const [state, setState] = useState(initState)

  return (
    <LanguageContext.Provider value={state}>
      {props.children}
    </LanguageContext.Provider>
  )
}

App.js

import React, { useContext } from 'react'
import { LanguageContextProvider, LanguageContext } from './LanguageContextManagement'

function App() {

  const state = useContext(LanguageContext)

  return (
    <LanguageContextProvider>
      <button onClick={() => state.setLanguage('pk')}>
        Current Language is: {state.language}
      </button>
    </LanguageContextProvider>
  )
}

export default App

1
Bunu yapıyorum ve çocuğumun içindeki set () => {}
işlevim

5
Netleştirmek için, ben senin örnekte düşünüyorum state.setLanguage('pk')çünkü hiçbir şey yapmaz const state = useContext(LanguageContext)dışındadır LanguageContextProvider. Sorunumu, sağlayıcıyı bir seviye yukarı taşıyarak ve ardından useContextbir çocuk üzerinde bir seviye aşağıda kullanarak çözdüm.
Alejandro Corredor

2
Eğer aynı zamanda böyle bağlam tüketici kullanabileceğiniz bağlam sağlayıcı bir seviye yukarı çıkmak için istemiyorsanız: <LanguageContext.Consumer> {value => /* access your value here */} </LanguageContext.Consumer>.
Mateen Kiani

3
LanguageContextMangement.js dosyasını düzenleme şeklinizi gerçekten beğendim. Bence bu, işleri yapmanın temiz bir yolu ve şimdi bunu yapmaya başlayacağım. Teşekkür ederim!
lustig

2
Takdiriniz için teşekkürler, bu şekilde çalışmaya devam etmem için beni gerçekten cesaretlendiriyor!
Mateen Kiani

2

Oldukça basit bir çözüm, sağlayıcınıza aşağıdaki gibi bir setState yöntemi ekleyerek bağlamınızda durum belirlemektir:

return ( 
            <Context.Provider value={{
              state: this.state,
              updateLanguage: (returnVal) => {
                this.setState({
                  language: returnVal
                })
              }
            }}> 
              {this.props.children} 
            </Context.Provider>
        )

Ve tüketicinizde, updateLanguage'ı şu şekilde arayın:

// button that sets language config
<Context.Consumer>
{(context) => 
  <button onClick={context.updateLanguage({language})}> 
    Set to {language} // if you have a dynamic val for language
  </button>
<Context.Consumer>
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.