Comment déclarer une variable globale dans React?

110

J'ai initialisé l' i18nobjet de traduction une fois dans un composant (premier composant qui se charge dans l'application). Ce même objet est requis dans tous les autres composants. Je ne veux pas le réinitialiser dans chaque composant. Quel est le chemin? Le rendre disponible pour la portée de la fenêtre n'aide pas car je dois l'utiliser dans la render()méthode.

Veuillez suggérer une solution générique pour ces problèmes et non une solution spécifique à i18n.

sapy
la source
1
Vous pouvez utiliser Reduxune Fluxbibliothèque qui peut gérer des données ou un état globalement. et vous pouvez le passer facilement à travers tous vos composants
vistajess
Une autre manière? Nous avons commencé le projet sans aucun framework React car c'était le début de React. Maintenant, connecter chaque composant au magasin Redux nécessitera un effort énorme.
sapy
3
Quelque chose comme React-Global fonctionnerait pour vous?
TwoStraws

Réponses:

51

Pourquoi n'essayez-vous pas d'utiliser Context ?

Vous pouvez déclarer une variable de contexte globale dans n'importe lequel des composants parents et cette variable sera accessible dans l'arborescence des composants par this.context.varname. Vous n'avez qu'à spécifier childContextTypeset getChildContextdans le composant parent et par la suite vous pouvez utiliser / modifier cela à partir de n'importe quel composant en spécifiant simplement contextTypesdans le composant enfant.

Cependant, veuillez en prendre note comme indiqué dans la documentation:

Tout comme les variables globales sont mieux évitées lors de l'écriture de code clair, vous devez éviter d'utiliser le contexte dans la plupart des cas. En particulier, réfléchissez à deux fois avant de l'utiliser pour "enregistrer le typage" et de l'utiliser au lieu de passer des accessoires explicites.

Naisheel Verdhan
la source
30
Fermer . mais Tous les composants n'ont pas de relation parent-enfant et le contexte ne fonctionne pas au-delà de cet arbre. Je veux i18n dans toute l'application.
sapy
@sapy c'est pourquoi vous devriez utiliser i18n comme état redux et non comme variable de contexte voir ma réponse ici: stackoverflow.com/questions/33413880/...
Fareed Alnamrouti
9
Réponse horrible.
r3wt
De mon côté, je recommanderais d'utiliser redux à la place, et spécialement pour les grandes applications
Hosny Ben
38

Au-delà de React

Vous ne savez peut-être pas qu'une importation est déjà globale . Si vous exportez un objet (singleton), il est alors globalement accessible en tant qu'instruction d'importation et il peut également être modifié globalement .

Si vous souhaitez initialiser quelque chose globalement mais vous assurer qu'il n'est modifié qu'une seule fois, vous pouvez utiliser cette approche singleton qui a initialement des propriétés modifiables, mais vous pouvez ensuite l'utiliser Object.freezeaprès sa première utilisation pour garantir son immuable dans votre scénario d'initialisation.

const myInitObject = {}
export default myInitObject

puis dans votre méthode init en le référençant:

import myInitObject from './myInitObject'
myInitObject.someProp = 'i am about to get cold'
Object.freeze(myInitObject)

Le myInitObjectsera toujours global car il peut être référencé n'importe où en tant qu'importation mais restera figé et jeté si quelqu'un tente de le modifier.

Si vous utilisez react-create-app

(ce que je cherchais en fait) Dans ce scénario, vous pouvez également initialiser proprement les objets globaux lors du référencement des variables d'environnement.

La création d'un fichier .env à la racine de votre projet avec des REACT_APP_variables préfixées à l'intérieur fait très bien. Vous pouvez référencer dans votre JS et JSX process.env.REACT_APP_SOME_VARselon vos besoins ET c'est immuable de par sa conception.

Cela évite d'avoir à définir window.myVar = %REACT_APP_MY_VAR%en HTML.

Voir plus de détails utiles à ce sujet directement sur Facebook:

https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables

Jason Sebring
la source
6
C'est honnêtement un excellent conseil, j'avais des problèmes avec mes jetons d'authentification depuis que j'utilise le rendu côté serveur. J'ai fini par charger depuis localstorage lors du premier chargement, puis je l'ai stocké dans un fichier de configuration séparé que je peux simplement importer. Très bonne réponse.
Huit
1
C'est la meilleure réponse à ce jour!
thiloilg
33

N'est pas recommandé mais .... vous pouvez utiliser componentWillMount de votre classe d'application pour ajouter vos variables globales à travers elle ... un peu comme ceci:

componentWillMount: function () {
    window.MyVars = {
        ajax: require('../helpers/ajax.jsx'),
        utils: require('../helpers/utils.jsx')
    };
}

considérez toujours cela comme un hack ... mais cela fera votre travail

btw componentWillMount s'exécute une fois avant le rendu, voir plus ici: https://reactjs.org/docs/react-component.html#mounting-componentwillmount

rokyed
la source
1
C'est un hack pour certains cas d'utilisation. J'intègre une application React dans le contexte d'une autre application non React dans mon entreprise. Cela semble être un excellent moyen d'exposer les méthodes publiques de l'application consommatrice. Ai-je tort? Y a-t-il un meilleur moyen?
mccambridge
2
Sachez également que componentWillMountc'est obsolète: reactjs.org/blog/2018/03/27/update-on-async-rendering.html
RyanNerd
@mccambridge Je sais que c'est tard mais j'enverrais une fonction de l'application non-React dans React que vous appelleriez pour envoyer les informations. Btw, vous pouvez le faire avec des iframes.
Nic Szerman
8

Créez un fichier nommé "config.js" dans le dossier ./src avec ce contenu:

module.exports = global.config = {
    i18n: {
        welcome: {
            en: "Welcome",
            fa: "خوش آمدید"
        }
        // rest of your translation object
    }
    // other global config variables you wish
};

Dans votre fichier principal "index.js" mettez cette ligne:

import './config';

Partout où vous avez besoin de votre objet, utilisez ceci:

global.config.i18n.welcome.en
Alireza
la source
5

Peut conserver les variables globales dans Webpack, c'est-à-dire dans webpack.config.js

externals: {
  'config': JSON.stringify(GLOBAL_VARIABLE: "global var value")
}

Dans js, le module peut lire comme

var config = require('config')
var GLOBAL_VARIABLE = config.GLOBAL_VARIABLE

J'espère que cela aidera.

Shashank Bhong
la source
2

Vous pouvez utiliser des mixins dans react https://facebook.github.io/react/docs/reusable-components.html#mixins .

Ramratan Gupta
la source
7
Remarque: si vous utilisez la syntaxe ES6 (qui est désormais recommandée) ou des composants purs, les mixins ne sont pas disponibles. La recommandation est de composer vos composants. medium.com/@dan_abramov/…
Aren
1
cela semble être une bonne idée car vous pouvez alors centraliser votre logique et passer plus tard à Flux d'autres types de stockage (par exemple, Localstorage ou DB côté client)
dcsan
2
Cette réponse aurait dû être développée pour inclure un exemple de code. Les liens peuvent se rompre.
vapcguy le
0

Voici une approche moderne, en utilisant globalThis, que nous avons adoptée pour notre application React Native .

globalThis est maintenant inclus dans ...


appGlobals.ts

// define our parent property accessible via globalThis. Also apply the TypeScript type.
var app: globalAppVariables;

// define the child properties and their types. 
type globalAppVariables = {
  messageLimit: number;
  // more can go here. 
};

// set the values.
globalThis.app = {
  messageLimit: 10,
  // more can go here.
};


// Freeze so these can only be defined in this file.
Object.freeze(globalThis.app);


App.tsx ( notre fichier de point d'entrée principal )

import './appGlobals'

// other code


anyWhereElseInTheApp.tsx

const chatGroupQuery = useQuery(GET_CHAT_GROUP_WITH_MESSAGES_BY_ID, {
  variables: {
    chatGroupId,
    currentUserId: me.id,
    messageLimit: globalThis.app.messageLimit, // 👈 used here.
  },
});
GollyJer
la source
-6

Je ne sais pas ce qu'ils essaient de dire avec ce truc "React Context" - ils me parlent grec, mais voici comment je l'ai fait:

Transporter des valeurs entre les fonctions, sur la même page

Dans votre constructeur, liez votre setter:

this.setSomeVariable = this.setSomeVariable.bind(this);

Ensuite, déclarez une fonction juste en dessous de votre constructeur:

setSomeVariable(propertyTextToAdd) {
    this.setState({
        myProperty: propertyTextToAdd
    });
}

Lorsque vous souhaitez le configurer, appelez this.setSomeVariable("some value");

(Vous pourrez peut-être même vous en sortir this.state.myProperty = "some value";)

Lorsque vous voulez l'obtenir, appelez var myProp = this.state.myProperty;

L'utilisation alert(myProp);devrait vous donner some value.

Méthode d'échafaudage supplémentaire pour transmettre des valeurs à travers les pages / composants

Vous pouvez attribuer un modèle à this(techniquement this.stores), vous pouvez donc le référencer avec this.state:

import Reflux from 'reflux'
import Actions from '~/actions/actions`

class YourForm extends Reflux.Store
{
    constructor()
    {
        super();
        this.state = {
            someGlobalVariable: '',
        };
        this.listenables = Actions;
        this.baseState = {
            someGlobalVariable: '',
        };
    }

    onUpdateFields(name, value) {
        this.setState({
            [name]: value,
        });
    }

    onResetFields() {
        this.setState({
           someGlobalVariable: '',
        });
    }
}
const reqformdata = new YourForm

export default reqformdata

Enregistrer à un dossier appelé storescomme yourForm.jsx.

Ensuite, vous pouvez le faire dans une autre page:

import React from 'react'
import Reflux from 'reflux'
import {Form} from 'reactstrap'
import YourForm from '~/stores/yourForm.jsx'

Reflux.defineReact(React)

class SomePage extends Reflux.Component {
    constructor(props) {
        super(props);
        this.state = {
            someLocalVariable: '',
        }
        this.stores = [
            YourForm,
        ]
    }
    render() {

        const myVar = this.state.someGlobalVariable;
        return (
            <Form>
                <div>{myVar}</div>
            </Form>
        )
    }
}
export default SomePage

Si vous aviez défini this.state.someGlobalVariableun autre composant en utilisant une fonction telle que:

setSomeVariable(propertyTextToAdd) {
    this.setState({
       myGlobalVariable: propertyTextToAdd
   });
}

que vous liez dans le constructeur avec:

this.setSomeVariable = this.setSomeVariable.bind(this);

la valeur dans propertyTextToAddserait affichée en SomePageutilisant le code ci-dessus.

vapcguy
la source
3
Désolé d'être nouveau ici, mais j'apprends juste React et je ne sais pas pourquoi cette réponse a reçu autant de
votes négatifs
3
@someoneuseless Exactement! Et c'est pourquoi je refuse de le supprimer et de me prosterner devant les masses. Ce n'est pas parce qu'ils veulent utiliser "Contexte" (quoi que ce soit) et ces autres objets amusants que tout le monde doit le faire. Tape m'en cinq!
vapcguy le