Como declarar uma variável global no React?

106

Inicializei o i18nobjeto de tradução uma vez em um componente (primeiro componente que carrega no aplicativo). Esse mesmo objeto é necessário em todos os outros componentes. Não quero reinicializá-lo em todos os componentes. Qual é o caminho? Torná-lo disponível para o escopo da janela não ajuda, pois preciso usá-lo no render()método.

Por favor, sugira uma solução genérica para esses problemas e não uma solução específica para i18n.

ensopado
fonte
1
Você pode usar Reduxou Fluxbiblioteca que pode lidar com dados ou estado globalmente. e você pode passá-lo facilmente por todos os seus componentes
vistajess
Qualquer outra maneira ? Começamos o projeto sem nenhum React Framework, pois era o primeiro dia do React. Agora, conectar todos os componentes à loja Redux exigirá um grande esforço.
sapy
3
Algo como reação global funcionaria para você?
TwoStraws

Respostas:

50

Por que você não tenta usar Context ?

Você pode declarar uma variável de contexto global em qualquer um dos componentes pai e essa variável estará acessível na árvore de componentes por this.context.varname. Você só precisa especificar childContextTypese getChildContextno componente pai e, a partir daí, pode usar / modificar isso de qualquer componente, apenas especificando contextTypesno componente filho.

No entanto, observe isso conforme mencionado nos documentos:

Assim como as variáveis ​​globais devem ser evitadas ao escrever um código claro, você deve evitar o uso de contexto na maioria dos casos. Em particular, pense duas vezes antes de usá-lo para "economizar digitação" e usá-lo em vez de passar acessórios explícitos.

Naisheel Verdhan
fonte
30
perto . mas o componente All não tem relacionamento pai-filho e o Contexto não funciona além dessa árvore. Quero i18n em todo o aplicativo.
sapy
@sapy é por isso que você deve usar i18n como estado redux, não como uma variável de contexto, veja minha resposta aqui: stackoverflow.com/questions/33413880/…
Fareed Alnamrouti
9
Resposta horrível.
r3wt
De minha parte, eu recomendaria usar redux em vez disso, e especialmente para grandes Apps
Hosny Ben
37

Beyond React

Você pode não estar ciente de que uma importação já é global . Se você exportar um objeto (singleton), ele ficará acessível globalmente como uma instrução de importação e também poderá ser modificado globalmente .

Se você deseja inicializar algo globalmente, mas garantir que seja modificado apenas uma vez, você pode usar essa abordagem singleton que inicialmente tem propriedades modificáveis, mas você pode usar Object.freezeapós seu primeiro uso para garantir que seja imutável em seu cenário de inicialização.

const myInitObject = {}
export default myInitObject

então em seu método init referenciando-o:

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

O myInitObjectainda será global, pois pode ser referenciado em qualquer lugar como uma importação mas permanecerá congelado e lançado se alguém tentar modificá-lo.

Se estiver usando react-create-app

(o que eu estava procurando na verdade) Neste cenário, você também pode inicializar objetos globais de forma limpa ao fazer referência a variáveis ​​de ambiente.

Criar um arquivo .env na raiz do seu projeto com REACT_APP_variáveis prefixadas dentro faz muito bem. Você pode fazer referência em seu JS e JSXprocess.env.REACT_APP_SOME_VAR conforme necessário E é imutável por design.

Isso evita ter que definir window.myVar = %REACT_APP_MY_VAR%em HTML.

Veja mais detalhes úteis sobre isso diretamente do Facebook:

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

Jason Sebring
fonte
6
Esta é, honestamente, uma ótima dica, eu estava tendo problemas com meus tokens de autenticação porque estou usando o Server Side Rendering. Acabei carregando do localstorage no primeiro carregamento e, em seguida, armazenando em um arquivo de configuração separado que posso simplesmente importar. Ótima resposta.
Oito
1
Esta é a melhor resposta até agora!
thiloilg
33

Não é recomendado, mas ... você pode usar o componentWillMount de sua classe de aplicativo para adicionar suas variáveis ​​globais através dele ... um pouco assim:

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

ainda considere isso um hack ... mas fará seu trabalho

btw componentWillMount é executado uma vez antes da renderização, veja mais aqui: https://reactjs.org/docs/react-component.html#mounting-componentwillmount

rokyed
fonte
1
É um hack para certos casos de uso. Estou integrando um aplicativo React ao contexto de outro aplicativo não React em minha empresa. Esta parece ser uma ótima maneira de expor métodos públicos para o aplicativo de consumo. Estou errado? Existe uma maneira melhor?
mccambridge
2
Esteja ciente de que o uso componentWillMountestá sendo suspenso: reactjs.org/blog/2018/03/27/update-on-async-rendering.html
RyanNerd
@mccambridge Sei que está atrasado, mas gostaria de enviar uma função do aplicativo não React para o React que você chamaria para enviar a informação. Aliás, você pode fazer isso com iframes.
Nic Szerman
6

Crie um arquivo chamado "config.js" na pasta ./src com este conteúdo:

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

No seu arquivo principal "index.js" coloque esta linha:

import './config';

Onde quer que você precise do seu objeto, use isto:

global.config.i18n.welcome.en
Alireza
fonte
5

Pode manter variáveis ​​globais em webpack, ou seja, em webpack.config.js

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

No módulo js pode ler como

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

Espero que isso ajude.

Shashank Bhong
fonte
2

Você pode usar mixins em react https://facebook.github.io/react/docs/reusable-components.html#mixins .

Ramratan Gupta
fonte
7
Nota: Se você estiver usando a sintaxe ES6 (que agora é recomendada) ou componentes puros, os mixins não estão disponíveis. A recomendação é compor seus componentes. medium.com/@dan_abramov/…
Aren,
1
isso parece uma boa ideia, pois então você pode centralizar sua lógica e depois mover para outros tipos de armazenamento de Fluxo (por exemplo, localstorage ou banco de dados do lado do cliente)
dcsan
2
Essa resposta deveria ter sido expandida para incluir um exemplo de código. Links podem quebrar.
vapcguy
0

Aqui está uma abordagem moderna, usando globalThis, que usamos para nosso aplicativo React Native .

globalThis agora está incluído em ...


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 ( nosso arquivo de ponto de entrada 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
fonte
-6

Não sei o que eles estão tentando dizer com essa coisa de "React Context" - eles estão falando grego, para mim, mas aqui está como eu fiz:

Carregando valores entre funções, na mesma página

Em seu construtor, vincule seu setter:

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

Em seguida, declare uma função logo abaixo do seu construtor:

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

Quando quiser configurá-lo, ligue this.setSomeVariable("some value");

(Você pode até ser capaz de escapar impune this.state.myProperty = "some value"; )

Quando você quiser pegar, ligue var myProp = this.state.myProperty;

Usar alert(myProp);deve dar a você some value.

Método de andaime extra para transportar valores entre páginas / componentes

Você pode atribuir um modelo a this(tecnicamente this.stores), para que possa referenciá-lo com 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

Guardar esta para uma pasta chamada storescomoyourForm.jsx .

Então você pode fazer isso em outra página:

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

Se você definiu this.state.someGlobalVariableem outro componente usando uma função como:

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

que você vincula no construtor com:

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

o valor em propertyTextToAddseria exibido SomePageusando o código mostrado acima.

vapcguy
fonte
3
Desculpe por ser novo aqui, mas acabei de aprender React e não tenho certeza por que esta resposta foi tão
rejeitada
3
@someoneuseless Exatamente! E é por isso que me recuso a excluí-lo e reverenciar as massas. Só porque eles querem usar "Contexto" (seja lá o que for) e esses outros objetos engraçados não significa que todo mundo precisa. Toca aqui!
vapcguy