Глобальные переменные в приложении universal React, узел выдает ошибку ссылки

#javascript #node.js #reactjs

#javascript #node.js #reactjs

Вопрос:

Я создаю универсальное приложение React с возможностями входа в Google. К сожалению, в Google нет универсальной библиотеки Google API ( gapi ), которую я мог бы использовать как на клиенте, так и на сервере.

Что я действительно пытаюсь здесь сделать, так это запустить мой серверный код и заставить Babel автоматически перекомпилировать мой код при внесении изменений и автоматически перезапустить сервер (как это делает nodemon, но с шагом компиляции). Я нашел пакет npm babel-watch , который будет делать именно это, однако он не интегрируется с webpack.

В моем шаблоне index.html файл, у меня есть следующий код в HTML <head> :

index.html

 <script type="text/javascript">
  window.gapiPromise = new Promise(resolve => window.gapiLoadedCallback = () => resolve(gapi))
</script>
<script src="https://apis.google.com/js/platform:auth2.js?onload=gapiLoadedCallback" async defer></script>
  

Приведенное выше создает новое Promise значение, которое разрешается значением gapi when platform.js загружается. Однако, поскольку это загружается только на клиенте gapi и gapiPromise не существует на сервере.

У меня также есть вход в Google в компоненте React, который использует глобальную gapiPromise переменную для рендеринга кнопки, когда gapi она готова к использованию:

GoogleSignIn.jsx

 import React from 'react'

// should only run on client
if (gapiPromise !== false) {
  gapiPromise.then(gapi => {
    gapi.auth2.init({
      client_id: '[removed]'
    })
  })
}

class GoogleSignIn extends React.Component {
  constructor(props) {
    super(props)
  }
  componentDidMount() {
    // should only run on client
    if (gapiPromise !== false) {
     gapiPromise.then(gapi => gapi.signin2.render('g-signin2', {
        'scope': 'email',
        'width': 160,
        'height': 50,
        'theme': 'light',
        'onsuccess': this.props.onSuccess,
        'onfailure': this.props.onFailure
      }))   
    }
  }
  render() {
    return (
      <div className="google-sign-in">
        <div id="g-signin2"></div>
      </div>
    )
  }
}

export default GoogleSignIn
  

Это отлично работает на клиенте, но когда я пытаюсь отобразить его на сервере, узел жалуется, что:

 /Users/jreznik/Sites/my-app/dist/server.js:3568
  if (gapiPromise !== false) {
      ^

ReferenceError: gapiPromise is not defined
    at Object.defineProperty.value (/Users/jreznik/Sites/my-app/dist/server.js:3568:6)
    at __webpack_require__ (/Users/jreznik/Sites/my-app/dist/server.js:20:30)
    at Object.defineProperty.value (/Users/jreznik/Sites/my-app/dist/server.js:3408:22)
    at __webpack_require__ (/Users/jreznik/Sites/my-app/dist/server.js:20:30)
    at Object.defineProperty.value (/Users/jreznik/Sites/my-app/dist/server.js:3354:24)
    at __webpack_require__ (/Users/jreznik/Sites/my-app/dist/server.js:20:30)
    at Object.defineProperty.value (/Users/jreznik/Sites/my-app/dist/server.js:194:21)
    at __webpack_require__ (/Users/jreznik/Sites/my-app/dist/server.js:20:30)
    at Object.<anonymous> (/Users/jreznik/Sites/my-app/dist/server.js:59:16)
    at Object.<anonymous> (/Users/jreznik/Sites/my-app/dist/server.js:131:31)
  

Я попытался gapiPromise добавить в этот файл префикс global. (т.Е. global.gapiPromise ), а затем определить global.gapiPromise = false в файле ввода моего сервера ( server.js ), но затем узел жалуется:

 /Users/jreznik/Sites/my-app/dist/server.js:3569
      global.gapiPromise.then(function (gapi) {
                        ^

TypeError: Cannot read property 'then' of undefined
  

Наконец, я смог заставить его работать, используя webpack DefinePlugin :

webpack.server.config.js

 ...

plugins: [
  new webpack.DefinePlugin({
    'window': {},
    'gapiPromise': false
  })
]

...
  

Но если я это сделаю, я не смогу использовать babel-watch пакет npm для автоматической перекомпиляции и перезапуска сервера.

Как я могу заставить узел перестать жаловаться на эти неопределенные глобальные переменные?

Ответ №1:

Это клиентская библиотека, что означает, что она предназначена только для использования с DOM из браузера. На самом деле это эквивалентно window.gapiPromise . И window объект представляет собой открытое окно в браузере.

Комментарии:

1. Спасибо за ваш ответ. Я понимаю это, однако я не собираюсь выполнять какой-либо код с gapi сервера, а просто визуализирую компонент на стороне сервера (который должен только визуализироваться <div className="google-sign-in"> <div id="g-signin2"></div> </div> ), а затем клиент будет использовать gapi для визуализации полной кнопки.

2. Вы не можете использовать его на стороне сервера, потому что это клиентская библиотека .

Ответ №2:

Хорошо, после нескольких часов возни с этим, я нашел решение через час после публикации этого вопроса.

Из моего файла GoogleSignIn.jsx я удалил код…

 // should only run on client
if (gapiPromise !== false) {
  gapiPromise.then(gapi => {
    gapi.auth2.init({
      client_id: '[removed]'
    })
  })
}
  

… и в шаблон:

 <script type="text/javascript">
    window.gapiPromise = new Promise(resolve => window.gapiLoadedCallback = () => {
        gapi.auth2.init({
            client_id: '[removed]'
        })
        resolve(gapi)
    })
</script>
<script src="https://apis.google.com/js/platform:auth2.js?onload=gapiLoadedCallback" async defer></script>
  

Не совсем уверен, почему Node не жалуется на дополнительные gapiPromise s в componentDidMount() методе, но это не так. Также я изменил if-оператор для проверки gapiPromise на:

if (typeof gapiPromise !== 'undefined') {