Jak zadeklarować zmienną globalną w React?

110

i18nRaz zainicjowałem obiekt translacji w komponencie (pierwszym komponencie, który ładuje się w aplikacji). Ten sam obiekt jest wymagany we wszystkich innych komponentach. Nie chcę ponownie inicjować go w każdym komponencie. Co to jest? Udostępnienie go zakresowi okna nie pomaga, ponieważ muszę go użyć w render()metodzie.

Proszę zasugerować ogólne rozwiązanie tego problemu, a nie konkretne rozwiązanie dla i18n.

sapy
źródło
1
Możesz użyć Reduxlub Fluxbiblioteki, która może obsługiwać dane lub stan globalnie. i możesz łatwo przejść przez wszystkie komponenty
vistajess
Jakiś inny sposób? Rozpoczęliśmy projekt bez żadnego React Framework, ponieważ był to początek Reacta. Teraz podłączenie każdego komponentu do sklepu Redux będzie wymagało ogromnego wysiłku.
sapy
3
Czy coś mogłoby dla Ciebie zadziałać globalnie ?
TwoStraws

Odpowiedzi:

51

Dlaczego nie spróbujesz użyć kontekstu ?

Możesz zadeklarować globalną zmienną kontekstową w dowolnym komponencie nadrzędnym, a zmienna ta będzie dostępna w drzewie komponentów przez this.context.varname. Musisz tylko określić childContextTypesi getChildContextw komponencie nadrzędnym, a następnie możesz użyć / zmodyfikować to z dowolnego komponentu, po prostu określając contextTypeskomponent podrzędny.

Należy jednak zwrócić uwagę na to, jak wspomniano w dokumentach:

Tak jak podczas pisania czystego kodu najlepiej unikać zmiennych globalnych, w większości przypadków należy unikać kontekstu. W szczególności, zastanów się dwa razy, zanim użyjesz go do „oszczędzenia pisania” i skorzystaj z niego zamiast przekazywania jawnych właściwości.

Naisheel Verdhan
źródło
30
blisko . ale wszystkie komponenty nie mają relacji rodzic-dziecko, a kontekst nie działa poza tym drzewem. Chcę i18n w całej aplikacji.
sapy
@sapy dlatego powinieneś używać i18n jako stanu redux, a nie jako zmiennej kontekstowej.Zobacz moją odpowiedź tutaj: stackoverflow.com/questions/33413880/ ...
Fareed Alnamrouti
9
Okropna odpowiedź.
r3wt
Ze swojej strony poleciłbym zamiast tego skorzystać z redux, a szczególnie dla dużych aplikacji
Hosny Ben
38

Beyond React

Możesz nie wiedzieć, że import jest już globalny . Jeśli eksportujesz obiekt (singleton), jest on wtedy dostępny globalnie jako instrukcja importu i może być również modyfikowany globalnie .

Jeśli chcesz zainicjować coś globalnie, ale upewnij się, że zostanie zmodyfikowany tylko raz, możesz użyć tego podejścia pojedynczego, które początkowo ma modyfikowalne właściwości, ale możesz użyć go Object.freezepo pierwszym użyciu, aby zapewnić jego niezmienność w scenariuszu inicjującym.

const myInitObject = {}
export default myInitObject

następnie w swojej metodzie init odwołując się do niego:

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

myInitObjectNadal będzie globalna, ponieważ może się odwoływać wszędzie jako import , ale pozostaną zamrożone i rzucać jeśli ktoś próbuje go zmodyfikować.

Jeśli używasz aplikacji react-create-app

(czego właściwie szukałem) W tym scenariuszu można również czysto inicjalizować obiekty globalne podczas odwoływania się do zmiennych środowiskowych.

Utworzenie pliku .env w katalogu głównym projektu z prefiksowymi REACT_APP_zmiennymi wewnątrz robi całkiem nieźle. Możesz odwoływać się w swoim JS i JSX, process.env.REACT_APP_SOME_VARjak potrzebujesz, ORAZ jest to niezmienne zgodnie z projektem.

Pozwala to uniknąć konieczności ustawiania window.myVar = %REACT_APP_MY_VAR%w HTML.

Zobacz więcej przydatnych informacji na ten temat bezpośrednio z Facebooka:

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

Jason Sebring
źródło
6
To naprawdę świetna wskazówka, ponieważ miałem problemy z moimi tokenami uwierzytelniania, ponieważ używam renderowania po stronie serwera. Skończyło się na ładowaniu z magazynu lokalnego przy pierwszym ładowaniu, a następnie zapisaniu go w oddzielnym pliku konfiguracyjnym, który mogę po prostu zaimportować. Świetna odpowiedź.
Ósmy
1
Jak dotąd to najlepsza odpowiedź!
thiloilg
33

Nie jest to zalecane, ale ... możesz użyć componentWillMount z klasy aplikacji, aby dodać przez nią zmienne globalne ... trochę tak:

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

nadal uważaj to za hack ... ale to wykona twoją pracę

btw componentWillMount wykonuje się raz przed renderowaniem, więcej informacji znajdziesz tutaj: https://reactjs.org/docs/react-component.html#mounting-componentwillmount

rokyed
źródło
1
To hack na pewne przypadki użycia. Integruję aplikację React w kontekście innej aplikacji innej niż React w mojej firmie. Wydaje się, że to świetny sposób na ujawnienie publicznych metod używanej aplikacji. Czy się mylę? Czy jest lepszy sposób?
mccambridge,
2
Należy również pamiętać, że funkcjacomponentWillMount ta jest przestarzała: awarejs.org/blog/2018/03/27/update-on-async-rendering.html
RyanNerd
@mccambridge Wiem, że to późno, ale wysłałbym funkcję z aplikacji non-React do React, którą zadzwoniłbyś, aby wysłać informacje. Przy okazji, możesz to zrobić za pomocą ramek iframe.
Nic Szerman
8

Utwórz plik o nazwie „config.js” w folderze ./src z następującą zawartością:

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

W swoim głównym pliku „index.js” umieść następującą linię:

import './config';

Gdziekolwiek potrzebujesz swojego obiektu, użyj tego:

global.config.i18n.welcome.en
Alireza
źródło
5

Może przechowywać zmienne globalne w webpacku, czyli w webpack.config.js

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

W module js można przeczytać jak

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

Mam nadzieję, że to pomoże.

Shashank Bhong
źródło
2

Możesz użyć mixinów w React https://facebook.github.io/react/docs/reusable-components.html#mixins .

Ramratan Gupta
źródło
7
Uwaga: jeśli używasz składni ES6 (która jest teraz zalecana) lub czystych komponentów, miksy nie są dostępne. Zaleca się komponowanie komponentów. medium.com/@dan_abramov/…
Aren
1
wydaje się to być dobrym pomysłem, ponieważ wtedy możesz scentralizować swoją logikę, a później przenieść do Flux inne typy pamięci (np. Localstorage lub DB po stronie klienta)
dcsan
2
Ta odpowiedź powinna zostać rozszerzona o przykładowy kod. Linki mogą się zrywać.
vapcguy
0

Oto nowoczesne podejście, które wykorzystaliśmy globalThisw naszym React Native aplikacji .

globalThis jest teraz uwzględniony w ...


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 ( nasz główny plik punktu wejścia )

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
źródło
-6

Nie wiem, co próbują powiedzieć za pomocą tego „kontekstu reakcji” - mówią do mnie po grecku, ale oto jak to zrobiłem:

Przenoszenie wartości między funkcjami na tej samej stronie

W swoim konstruktorze powiąż setera:

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

Następnie zadeklaruj funkcję tuż pod konstruktorem:

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

Jeśli chcesz to ustawić, zadzwoń this.setSomeVariable("some value");

(Możesz nawet ujść na sucho this.state.myProperty = "some value";)

Kiedy będziesz chciał, zadzwoń var myProp = this.state.myProperty;

Używanie alert(myProp);powinno ci daćsome value .

Dodatkowa metoda tworzenia szkieletów do przenoszenia wartości między stronami / komponentami

Możesz przypisać model do this(technicznie this.stores), więc możesz odwołać się do niego za pomocą 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

Zapisz to w folderze o nazwie storesjako yourForm.jsx.

Następnie możesz to zrobić na innej stronie:

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

Jeśli ustawiłeś this.state.someGlobalVariableinny komponent za pomocą funkcji takiej jak:

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

który łączysz w konstruktorze z:

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

wartość w propertyTextToAddzostanie wyświetlona przy SomePageużyciu kodu pokazanego powyżej.

vapcguy
źródło
3
Przepraszam, że jestem tu nowy, ale po prostu uczę się Reagować i nie jestem pewien, dlaczego ta odpowiedź ma tak wielu negatywnych opinii
ktoś bezużyteczny
3
@someoneuseless Exactly! I dlatego odmawiam usunięcia tego i kłaniam się masom. Tylko dlatego, że chcą używać „kontekstu” (cokolwiek to jest) i innych zabawnych obiektów, nie oznacza, że ​​wszyscy tego potrzebują. Piątka!
vapcguy