Wie aktualisiere ich den Reaktionskontext innerhalb einer untergeordneten Komponente?


100

Ich habe die Spracheinstellungen im Kontext wie unten

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

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

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

export default LanguageProvider;

Mein Bewerbungscode lautet wie folgt

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

Meine Seite enthält eine Komponente zum Wechseln der Sprache

<MyPage>
  <LanguageSwitcher/>
</MyPage>

LanguageSwitcher In diesem MyPageFall muss der Kontext aktualisiert werden, um die Sprache wie unten beschrieben in 'jp' zu ändern

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;

Wie kann ich den Kontext innerhalb der LanguageSwitcher-Komponente aktualisieren?


Hast du das gelesen facebook.github.io/react/docs/context.html#updating-context Vielleicht ist dies etwas besser geeignet für Zustand nicht Kontext
Azium

@azium Ja .. In diesem Dokument wird der Kontext von der Komponente selbst aktualisiert, oder es wird ein Blog-Link im Dokument hinzugefügt, der den Kontext enthält, der als Requisiten an den Kontextanbieter übergeben wurde. Ich muss ihn von der
untergeordneten

Uhh nein, das Dokument sagt, dass der Kontext nicht verwendet werden soll, wenn Sie ihn aktualisieren müssen. "Tu es nicht" um genau zu sein. Ich werde wiederholen, verwenden Sie Zustand nicht Kontext
Azium

2
@LondonRob Welche kanonische Antwort suchen Sie? IMO sieht der Inhalt der Dokumente für mich gut aus. Wenn Sie den Kontext in einem untergeordneten Element festlegen möchten, erstellen Sie einfach einen Setter in der Komponente des Anbieters und übergeben Sie diesen an einen untergeordneten Verbraucher. Rufen Sie diesen Setter dann im untergeordneten Consumer auf und setzen Sie ihn auf die Daten, die sich im untergeordneten Element befinden. Bleibt immer noch bei Reacts Idee, Daten anzuheben.
Andrew Li

2
@azium nur ein Kopf hoch zu anderen, die diesen Kommentar all diese Jahre später lesen. Das Aktualisieren des Kontexts von einer untergeordneten
lustig

Antworten:


228

Haken verwenden

Hooks wurden in 16.8.0 eingeführt, sodass für den folgenden Code eine Mindestversion von 16.8.0 erforderlich ist (scrollen Sie nach unten, um das Beispiel für Klassenkomponenten anzuzeigen). CodeSandbox-Demo

1. Festlegen des übergeordneten Status für den dynamischen Kontext

Um einen dynamischen Kontext zu haben, der an die Verbraucher weitergegeben werden kann, verwende ich zunächst den Status der Eltern. Dies stellt sicher, dass ich eine einzige Quelle der Wahrheit habe. Zum Beispiel sieht meine übergeordnete App folgendermaßen aus:

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

  return (
    ...
  );
};

Das languageist im Zustand gespeichert. Wir werden beide languageund die Setter-Funktion setLanguagespäter über den Kontext übergeben.

2. Erstellen eines Kontexts

Als nächstes habe ich einen Sprachkontext wie folgt erstellt:

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

Hier setze ich die Standardeinstellungen für language('en') und eine setLanguageFunktion, die vom Kontextanbieter an die Verbraucher gesendet wird. Dies sind nur Standardeinstellungen, und ich werde ihre Werte angeben, wenn die Anbieterkomponente im übergeordneten Element verwendet wird App.

Hinweis: Das LanguageContextbleibt gleich, ob Sie

3. Erstellen eines Kontextkonsumenten

Damit der Sprachumschalter die Sprache einstellen kann, sollte er über den Kontext Zugriff auf die Spracheinstellungsfunktion haben. Es kann ungefähr so ​​aussehen:

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

Hier setze ich nur die Sprache auf 'jp', aber Sie haben möglicherweise Ihre eigene Logik, um Sprachen dafür festzulegen.

4. Einwickeln des Verbrauchers in einen Anbieter

Jetzt werde ich meine Sprachumschaltkomponente in a rendern LanguageContext.Providerund die Werte, die über den Kontext gesendet werden müssen, an eine tiefere Ebene übergeben. So sehen meine Eltern Appaus:

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

Wenn Sie jetzt auf den Sprachumschalter klicken, wird der Kontext dynamisch aktualisiert.

CodeSandbox-Demo

Klassenkomponenten verwenden

Die neueste Kontext-API wurde in React 16.3 eingeführt und bietet eine hervorragende Möglichkeit, einen dynamischen Kontext zu erstellen. Der folgende Code erfordert eine Mindestversion von 16.3.0. CodeSandbox-Demo

1. Festlegen des übergeordneten Status für den dynamischen Kontext

Um einen dynamischen Kontext zu haben, der an die Verbraucher weitergegeben werden kann, verwende ich zunächst den Status der Eltern. Dies stellt sicher, dass ich eine einzige Quelle der Wahrheit habe. Zum Beispiel sieht meine übergeordnete App folgendermaßen aus:

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

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

  ...
}

Das languagewird im Status zusammen mit einer Sprachsetzermethode gespeichert, die Sie möglicherweise außerhalb des Statusbaums aufbewahren.

2. Erstellen eines Kontexts

Als nächstes habe ich einen Sprachkontext wie folgt erstellt:

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

Hier setze ich die Standardeinstellungen für language('en') und eine setLanguageFunktion, die vom Kontextanbieter an die Verbraucher gesendet wird. Dies sind nur Standardeinstellungen, und ich werde ihre Werte angeben, wenn die Anbieterkomponente im übergeordneten Element verwendet wird App.

3. Erstellen eines Kontextkonsumenten

Damit der Sprachumschalter die Sprache einstellen kann, sollte er über den Kontext Zugriff auf die Spracheinstellungsfunktion haben. Es kann ungefähr so ​​aussehen:

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

Hier setze ich nur die Sprache auf 'jp', aber Sie haben möglicherweise Ihre eigene Logik, um Sprachen dafür festzulegen.

4. Einwickeln des Verbrauchers in einen Anbieter

Jetzt werde ich meine Sprachumschaltkomponente in a rendern LanguageContext.Providerund die Werte, die über den Kontext gesendet werden müssen, an eine tiefere Ebene übergeben. So sehen meine Eltern Appaus:

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

Wenn Sie jetzt auf den Sprachumschalter klicken, wird der Kontext dynamisch aktualisiert.

CodeSandbox-Demo


Was ist der Zweck der Standardwerte, mit denen Sie den Kontext initialisieren? Werden diese Standardeinstellungen nicht immer von der überschrieben Provider?
Öko

@ecoe korrekt, aber falls der Anbieter nein übergibt value, werden die Standardeinstellungen vom Verbraucher verwendet.
Divyanshu Maithani

1
Warum beschränken sich Kontexte darauf, einen einfachen Wert festzulegen / zu erhalten? Das scheint sehr ineffizient zu sein. Ein besseres Beispiel wäre, einen Kontext mit Standard als Objekt hervorzuheben und das Objekt entsprechend zu aktualisieren.
AlxVallejo

Es ist möglich, Kontexte für das zu verwenden, was Sie erwähnen. Das Beispiel in der Antwort war die Rückgabe basierend auf der Frage. Daher enthält es der Kürze halber nur einen einzigen Wert.
Divyanshu Maithani

1
Typoskript beschwert sich in meinem Fall, wenn setLanguage keine Parameter hat. setLanguage: (language: string) => {}funktioniert bei mir.
alex351

49

Die obige Antwort von Maithani ist eine großartige Lösung, aber das ist eine Klassenkomponente ohne die Verwendung von Haken. Da es von React empfohlen wird, funktionale Komponenten und Hooks zu verwenden , werde ich es mit useContext- und useState-Hooks implementieren. So können Sie den Kontext innerhalb einer untergeordneten Komponente aktualisieren.

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
Ich mache das und meine Set-Funktion in meiner untergeordneten Komponente ist immer die, die wir ursprünglich beim Erstellen des Kontexts deklariert haben:() => {}
Alejandro Corredor

5
Zur Verdeutlichung denke ich, dass in Ihrem Beispiel state.setLanguage('pk')nichts getan wird, da const state = useContext(LanguageContext)es außerhalb des LanguageContextProvider. Ich habe mein Problem gelöst, indem ich den Anbieter um eine Ebene nach oben verschoben und dann useContextfür ein Kind eine Ebene darunter verwendet habe.
Alejandro Corredor

2
Wenn Sie Ihren Kontextanbieter nicht um eine Ebene nach oben verschieben möchten, können Sie auch den Kontextkonsumenten wie folgt verwenden : <LanguageContext.Consumer> {value => /* access your value here */} </LanguageContext.Consumer>.
Mateen Kiani

3
Mir gefällt die Art und Weise, wie Sie die Datei LanguageContextMangement.js organisieren, sehr gut. Das ist meiner Meinung nach eine saubere Art, Dinge zu tun, und ich werde jetzt damit beginnen. Vielen Dank!
lustig

2
Vielen Dank für die Anerkennung, die mich wirklich ermutigt, so weiter zu arbeiten!
Mateen Kiani

2

Eine recht einfache Lösung besteht darin, den Status in Ihrem Kontext festzulegen, indem Sie eine setState-Methode wie folgt in Ihren Provider aufnehmen:

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

Rufen Sie bei Ihrem Verbraucher updateLanguage folgendermaßen auf:

// 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>
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.