В React, как определить, рендерит ли мой компонент клиент или сервер?

Я создаю изоморфное приложение, но я использую сторонний компонент, который отображается только на клиенте. Поэтому, особенно для этого компонента, мне нужно отображать его только при рендеринге на клиенте.

Как определить, что я нахожусь на клиенте или на сервере? Я ищу что-то вроде isClient() или isServer().

Ответ 1

Внутри React для этого использует утилиту с именем ExecutionEnvironment. Он реализует несколько полезных свойств, таких как canUseDOM и canUseEventListeners. Решение по сути только то, что предлагается здесь, хотя.

Реализация canUseDOM

var canUseDOM = !!(
  (typeof window !== 'undefined' &&
  window.document && window.document.createElement)
);

Я использую это в моем приложении, как это

var ExecutionEnvironment = require('react/node_modules/fbjs/lib/ExecutionEnvironment');
...
render() {
  <div>{ ExecutionEnvironment.canUseDOM ? this.renderMyComponent() : null }</div>
}

РЕДАКТИРОВАТЬ Это недокументированная функция, которая не должна использоваться напрямую. Его местоположение, вероятно, будет меняться от версии к версии. Я поделился этим как способом сказать "это лучшее, что вы можете сделать", показав, что команда Facebook использует внутри страны. Возможно, вы захотите скопировать этот код (он крошечный) в ваш собственный проект, поэтому вам не придется беспокоиться о том, чтобы не отставать от его местоположения от версии к версии или о возможных критических изменениях.

ДРУГОЕ РЕДАКТИРОВАНИЕ Кто-то создал пакет npm для этого кода. Я предлагаю использовать это.

npm install exenv --save

Ответ 2

Две вещи, которые могут иметь отношение к делу:

Многие проекты используют некоторые соглашения, в которых они устанавливают глобальные логические значения SERVER или CLIENT, чтобы весь ваш код мог переключаться на его основе. В вашем серверном комплекте установите глобальный, как в этом проекте

global.__SERVER__ = true;

И в вашем клиентском пакете установите для глобального клиента значение true, чего вы можете достичь одним способом с помощью Webpack DefinePlugin

new webpack.DefinePlugin({
  __CLIENT__: true
})

При описанном выше подходе вы можете переключаться на основе этой переменной в willMount или выполнять рендеринг для выполнения одной операции на сервере, а другой - на клиенте.

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

Ответ 3

Вы можете использовать события реакции жизненного цикла (например: componentDidMount) для обнаружения рендеринга на стороне сервера/клиента.

Примеры

Как крюк

import { useState, useEffect } from 'react'

function useIsServer () {
  const [isServer, setIsServer] = useState(true)
  useEffect(() => {
    setIsServer(false)
  }, [])
  return isServer
}

использование

Смотрите ниже (функциональный компонент)

Как функциональный компонент

import useIsServer from './above'

function ServerOnly ({ children = null, onClient = null }) {
  const isServer = useIsServer()
  return isServer
    ? children
    : onClient
}

использование

<ServerOnly
  children='This String was rendered on the server'
  onClient='This String was rendered on the client'
/>

Как компонент класса

class ServerOnly extends React.Component {
  constructor (props) {
    super(props)
    this.state = {
      isServer: true
    }
  }

  componentDidMount() {
    this.setState({
      isServer: false
    })
  }

  render () {
    const { isServer } = this.state
    const { children, onClient } = this.props
    return isServer
      ? children
      : onClient
  }
}

использование

<ServerOnly
  children='This String was rendered on the server'
  onClient='This String was rendered on the client'
/>

Ответ 4

Вы также можете использовать componentDidMount(), так как этот метод жизненного цикла не запускается, когда страница отображается на стороне сервера.

Ответ 5

На самом верхнем уровне иерархии элементов сервера можно добавить ServerContext такой как этот:

class ServerContext extends React.Component {
  getChildContext() { return { isServer: true }; }
  render() { return React.Children.only(this.props.children); }
}

ServerContext.propTypes = {
  children: React.PropTypes.node.isRequired,
};

ServerContext.childContextTypes = {
  isServer: React.PropTypes.bool.isRequired,
};

// Create our React application element.
const reactAppElement = (
  <ServerContext>
    <CodeSplitProvider context={codeSplitContext}>
      <ServerRouter location={request.url} context={reactRouterContext}>
        <DemoApp />
      </ServerRouter>
    </CodeSplitProvider>
  </ServerContext>
);

При этом должна быть возможность прочитать isServer из контекста следующим образом:

const Layout = (_, { isServer }) => (
  // render stuff here
);

Ответ 6

Вы можете создать одну полезную утилиту с помощью пакета exenv.

import { canUseDOM } from 'exenv';

export function onClient(fn: (..._args: any[]) => any): (..._args: any[]) => any {
    if (canUseDOM) {
        return fn;
    }

    if (process.env.NODE_ENV === 'development') {
        console.log('Called ${fn.name} on client side only');
    }

    return (): void => {};
}

И используйте это так

function my_function_for_browser_only(arg1: number, arg2: string) {}

onClient(my_function_for_browser_only)(123, "Hi !");

И функция будет вызываться только на стороне клиента, и она будет регистрировать на стороне сервера, что эта функция была вызвана на стороне клиента, если вы установите NODE_ENV=development

(Это машинопись, убери типы для JS :))

Ответ 7

Вы также можете просто использовать React-хук use-ssr

import useSSR from 'use-ssr'

const App = () => {
  var { isBrowser, isServer } = useSSR()

  // Want array destructuring? You can do that too!
  var [isBrowser, isServer] = useSSR()

  /*
   * In your browser chrome devtools console you should see
   * > IS BROWSER: 👍
   * > IS SERVER: 👎
   *
   * AND, in your terminal where your server is running you should see
   * > IS BROWSER: 👎
   * > IS SERVER: 👍
   */
  console.log('IS BROWSER: ', isBrowser ? '👍' : '👎')
  console.log('IS SERVER: ', isServer ? '👍' : '👎')
  return (
    <>
      Is in browser? {isBrowser ? '👍' : '👎'}
      <br />
      Is on server? {isServer ? '👍' : '👎'}
    </>
  )
}

пример

Ответ 8

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

var isBrowser = window!==undefined